导语
缓存由于高并发和高性能的特性,经常被用于提高数据库的性能。但是业务往往需要花费大量精力来维护缓存和数据库的一致性。由IEG技术运营部/存储与计算资源中心 & 腾讯云数据库团队联合打造的 TendisX 冷热混合存储, 使用 Redis 作为缓存, Tendisplus 作为后端持久化数据库,并且自动将热数据加载到缓存,冷数据从缓存侧淘汰。业务侧使用完全透明,无需考虑缓存不一致的问题,从而更方便业务开发。
本文首先介绍由 IEG 技术运营部团队 & 腾讯云数据库团队开发的TendisX 冷热混合存储方案的整体架构,然后分析常用的解决缓存和数据库一致的方案存在的一些问题,最后从缓存一致性、击穿、雪崩、穿透、故障恢复、性能、业务接入复杂度等方面对这几个方案进行对比。
在数据高并发读写场景, 数据库大多情况无法满足业务要求。 Redis 由于数据结构简单,读取速度快, 常常作为后端数据库的缓存使用。但是在使用缓存的时候,业务需要考虑: 1)是先写缓存还是先写数据库? 2)写请求是否更新缓存?等等问题。
TendisX 是 IEG 技术运营部自研,主要解决 Redis 内存占用高和缓存不一致问题的 NoSQL 存储系统。
TendisX 冷热混合存储架构核心组件由 Proxy 、缓存Redis、存储Tendisplus组成,其中每个组件的功能介绍如下:
Proxy组件:负责对客户端请求进行路由分发,将不同的Key的命令分发到正确的分片,同时Proxy还负责了部分监控数据的采集,以及高危命令在线禁用等功能。
缓存Redis:缓存Redis组件基于 Redis 4.0 进行开发。Redis 具有以下功能:缓存全量的 Key;仅淘汰冷 Key 的 Value; 自动从 Tendisplus 恢复数据; Redis 限速。
存储Tendisplus:Tendisplus 是腾讯基于 RocksDB 自研的 兼容 Redis 协议的KV存储引擎,该引擎已经在腾讯集团内部运营多年,性能和稳定性得到了充分的验证。在混合存储系统中主要负责全量数据的存储和读取,以及数据备份,增量日志备份等功能。

在使用缓存的过程中往往会由于不正确的使用方式,导致缓存和后端数据库数据不一致,本章将介绍缓存的常见使用方式以及业界的解决缓存和数据库不一致的方案。
缓存作为一种广泛使用的技术, 常见的主要有以下三种使用模式: Cache Aside Pattern, Cache Through Pattern 和 Cache Back Pattern(具体介绍可以参考这篇文章Things You Should Know About Database Caching)。Cache Aside Pattern 是最常用的模式,Facebook 在 Scaling MemCache at Facebook 论文中也使用这种缓存模式。Cache Aside Pattern 采用先写数据库,再删除缓存,然而在某些情况也可能导致缓存和数据库的不一致。
在缓存+数据库的架构中,缓存和数据库的操作总共有以下几种顺序:
通过上面分析可以得出,业务方无论是先写缓存还是先写数据库,最终都有可能出现缓存和数据库的不一致。那到底如何才能使缓存和数据库达到一致的状态。针对这个问题, 业界通常使用下面两种解决方案: 1)采用延时双删策略 2)异步更新缓存策略 。
延时双删除策略会在写库前后删除缓存中数据,并且给缓存数据设置合理的过期时间, 从而可以保证最终一致性。写流程具体如下:

为什么会休眠一段时间,这里主要是防止: 在写请求删除缓存但还未成功写入数据库后,读请求可能将旧值加载到缓存。
读流程:先读缓存,当缓存未命中,再从数据库中读取,然后再写入缓存。
延时双删策略虽然解决了上述讨论的缓存和数据库不一致的问题,但是以下问题:
异步更新缓存策略通常会使用一个异步同步组件(比如 canal 或者我司的 MySync), 通过解析从库的 binlog 获取数据,并通过消息队列将其串行化写入到缓存。

写流程:
读流程:
异步更新缓存策略通过消息队列的方式将并行化的操作串行化,从而解决了并发问题。但是也引入一些其他问题,比如缓存相对主库落后延迟较大。
TendisX 中, 业务仅仅可以访问 Redis, 所有的数据恢复工作对业务不可见。因此业务可以简单的将其当做一个Redis 服务来用,而不用考虑各种缓存的问题。
Redis 与社区版不同的地方:
写请求流程如下:

读请求的处理流程:

下面主要从缓存问题、主备切换场景、性能以及业务接入复杂度方面,对比 TendisX 冷热存储和延时双删策略、异步刷新策略。
TendisX 在使用过程中不会出现缓存穿透、缓存击穿、缓存雪崩和缓存不一致等问题。但是延时双删策略和异步刷新策略要看业务方如何处理,解决这些问题。
缓存穿透是指查询数据库不存在的数据。当查询缓存和数据库都没有的 Key 时,由于 TendisX 中 redis 缓存用户的所有 key, 因此可以直接通过 redis 判断该 key 是否存在,不会有缓存穿透的问题。
如果某些 Key 可能会在某些时间点被超高并发地访问,则它是一种非常“热点”的数据。缓存击穿是指大量的请求同时查询一个失效的热点数据,导致请求全部转发到数据库。
当某个热点的 Key 失效时,当下次具有大量该 Key 的请求时,TendisX 执行流程如下:
缓存雪崩是指某一时刻发生大规模的缓存失效的情况,比如缓存故障或者缓存采用相同的过期时间,缓存在某一时刻同时失效, 请求全部转发给数据库。
TendisX 可以利用 Redis 集群模式实现高可用,即使缓存故障,也可以及时通过备缓存恢复服务。另外 TendisX 的用户请求统一由缓存服务,请求不会转发给后端数据库。
延时双删策略
异步更新缓存策略
通过解析从库的 binlog ,再通过消息队列串行化发给缓存, 缓存较主库延迟较大。比如大概率会出现业务写入主库更新数据,缓存还未更新,业务从缓存中得到旧值。
TendisX 混合存储
对于并发读写,经常会出现缓存不一致的场景:
请求 A 进行写操作
请求 B 进行读操作
redis 是单线程串行化执行用户的请求, 当写操作先执行。
如果需要从 Tendisplus 中恢复数据,则请求 A 和 B 都阻塞。当数据恢复后,再唤醒阻塞的请求 A 和 B, 缓存和后端 Tendisplus 最终是一致的。
如果不需要从 Tendisplus 中恢复数据,则请求 A 和 B 肯定是顺序执行的,不会出现不一致的行为。
当 TendisX 中 redis master 故障时, 后端的 Tendisplus 可以切到对应的 redis slave。主备之间通过异步复制,可能会造成部分数据丢失。 TendisX 在 redis 主备和 redis 和 Tendisplus 同步的时候提供了限速,也就是说 slave 最多比 master 慢 2秒的数据(业务方在最差情况下丢失的数据)。
当 TendisX 中后端 Tendisplus 主故障时, Tendisplus 备可以切换到对应的 redis master,从 redis master 继续同步数据,数据不会丢失。
对于延时双删策略和异步刷新策略
如果缓存故障,并且假设缓存使用 redis, 也可以使用 redis 自动故障切换。但是由于是先写数据库,不会有数据丢失。
如果数据库故障,如果数据库主备间使用异步复制,也会有一定的数据丢失。
数据可容忍的丢失程度。
延时双删策略和异步刷新策略的劣势:
TendisX的一些性能优势:
延时双删策略需要业务同时操作缓存和数据库,同时业务方也要保证第二次删除缓存失败后的重试,对业务来说使用比较麻烦,不友好。
业务方可以将 TendisX 当做一个 Redis 缓存来用,不用担心各种缓存问题。
下面通过一个表来直观的对比 TendisX 冷热混合存储和另外两种策略:
