一文弄懂Redis缓存一致性最佳实践参考案例( 二 )


这个方案的缺点是:在数据库数据存在高并发更新且缓存读取流量较大的情况下,会有小概率存在缓存中存储的是旧版本数据的情况 。
通常的解法有四种:
1)限制缓存有效时间:设定缓存的过期时间,比如 15 分钟 。即表示我们最多接受缓存在 15 分钟的时间范围内是旧的 。
2)小概率缓存重加载:根据流量比设定一定比例的缓存重加载,以保证大流量情况下的缓存数据的一致性 。比如 1%的比例,这同时还可以帮助数据库得到充分的预热 。
3)结合业务特点:根据业务的特点做一些设计,比如:
针对营销的场景:在商品详情页/确认订单页的优惠计算时使用缓存,而在下单时不使用缓存 。这可以让极端情况发生时,不产生过大的业务损失 。
针对库存的场景:读取到旧版本的数据只是会在商品已售罄的情况下让多余的流量进入到下单而已,下单时的库存扣减是操作数据库的,所以不会有业务上的损失 。
4)两次删除:D1 步删除缓存的操作执行两次,且中间有一定的间隔,比如 30 秒 。这两次动作的触发都是由“缓存管理组件”发起的,所以可以由它支持 。
最佳实践二:带版本写入针对象商品信息缓存这种更新频率低、数据一致性要求较高且缓存读取流量很高的场景,通常会采用带版本更新的方式,整体的执行逻辑如下图如示:

一文弄懂Redis缓存一致性最佳实践参考案例

文章插图
 
和“数据库变更后失效缓存”方案最大的差异在 W4 步和 D1 步,需要缓存层提供带版本写入的 API,即仅当写入数据版本较新时可以写入成功,否则写入失败 。这同时也要求我们在数据库增加数据版本的信息 。
这个方案的最终一致性效果比较好,仅在极端情况下(新版本写入后数据丢失了,后续旧版本的写入就会成功)存在缓存中存储的是旧版本数据的可能 。在 D1 步使用写入而不是使用删除可以极大程度的避免这个极端情况的出现,同时由于该方案适用于缓存读取流量很高的场景,还可以避免缓存被删除后 W3 步短时间大量请求穿透到 DB 。
总结与展望对于缓存与数据库分离的场景,在结合了业界多家公司的实践经验以及 ROI 权衡之后,前述的两个最佳实践是被应用的最为广泛的,尤其是最佳实践一,应该作为我们日常应用的首选 。同时,为了最大限度的避免每个最佳实践背后可能发生的不一致性问题,我们还需要切合业务的特点,在关键的场景上做一些保障一致性的设计(比如前述的营销在下单时使用数据库读而不是缓存读),这也显得尤为重要(毕竟如“背景”中所述,并不存在完美的技术方案) 。
除了缓存与数据库分离的方案,还有两个业界已经应用的方案也值得我们借鉴:
阿里 XKV简单来讲就是在数据库上部署一个 Memcache 的 Server,它直接绕过数据库层直接访问存储引擎层(如:InnoDB),同时使用 KV client 来进行数据的访问 。它的特点是数据实际上与数据库是强一致的,性能可以比使用 SQL 访问数据库提升 5~10 倍 。缺点也很明显,只能通过主键或者唯一键来访问数据(这只是相对 SQL 来说的,大多数缓存本来也就是 KV 访问协议) 。
腾讯 DCache介绍资料 。不用自行维护缓存与数据库两套存储,给开发人员统一的一套数据视图,由 DCache 在缓存更新后自行持久化数据 。缺点是支持的数据结构有限( key-value,k-k-row,list,set,zset ),未来也很难支持形如数据库表一样复杂的数据结构 。
作者:得物技术
链接:
https://juejin.cn/post/7051772135584301092




推荐阅读