一致性问题:缓存更新策略
About 3 min
一致性问题:缓存更新策略
概览
内存淘汰 | 超时剔除 | 主动更新 | |
---|---|---|---|
说明 | 不用自己维护,利用 Redis 内存淘汰机制,当内存不足时自动淘汰部分数据。下次查询时更新缓存。 | 给缓存数据添加 TTL 时间,到期后自动删除缓存。下次查询时更新缓存。 | 编写业务逻辑,再修改数据库的同时,更新缓存 |
一致性 | 差 | 一般 | 好 |
维护成本 | 无 | 低 | 高 |
业务场景:
- 低一致性需求:使用内存淘汰机制。例如店铺类型等查询。
- 高一致性需求:主动更新,并以超时剔除作为兜底方案。例如店铺详情查询的缓存。
主动更新策略
Cache Aside Pattern
由缓存的调用者,在更新数据库的同时更新缓存。
需要开发者自己编码,但是可控性很高
需要考虑的问题
删除缓存还是更新缓存?
更新缓存:每次更新数据库都更新缓存,无效读写多 ,且会不一致❌
先更新数据库、再更新缓存
先更新缓存、再更新数据库
删除缓存:更新数据库时,让缓存失效,查询时再更新缓存🉑
如何保证缓存与数据库操作的同时成功或失败?
- 单体系统,将缓存与数据库操作放在一个事务
- 分布式系统,利用 TCC 等分布式事务方案
先操作缓存还是先操作数据库?多线程并发访问
先删除缓存,再操作数据库 ❌
先操作数据库,再删除缓存🉑
一致性问题,操作数据库比操作 redis慢得多,所以先操作数据库出现一致性问题的概率更小
且即使删除操作失败了,也有过期时间可以作为保障兜底(即使删除操作失败了,超时以后也可以删除)
缓存删除失败的方案:异步操作缓存
重试机制
我们可以引入消息队列,将第二个操作(删除缓存)要操作的数据加入到消息队列,由消费者来操作数据。
- 如果应用删除缓存失败,可以从消息队列中重新读取数据,然后再次删除缓存,这个就是重试机制。当然,如果重试超过的一定次数,还是没有成功,我们就需要向业务层发送报错信息了。
- 如果删除缓存成功,就要把数据从消息队列中移除,避免重复操作,否则就继续重试。
举个例子,来说明重试机制的过程。
订阅 MySQL binlog 再操作缓存
Canal
Read/Write Through Pattern
方案是由某种第三方服务提供,但是这样的服务很少
Write Behind Caching Pattern 写回
调用者只操作缓存,由其他线程异步的将缓存数据持久化道数据库,保证最终一致性
一致性和可靠性都存在一定的问题。
MESI 协议是一个基于失效的缓存一致性协议,是支持写回(write-back)缓存的最常用协议。也称作伊利诺伊协议 (Illinois protocol,因为是在伊利诺伊大学厄巴纳-香槟分校被发明的[1])。与写直达(write through)缓存相比,回写缓冲能节约大量带宽。总是有“脏”(dirty)状态表示缓存中的数据与主存中不同。MESI 协议要求在缓存不命中(miss)且数据块在另一个缓存时,允许缓存到缓存的数据复制。与 MSI 协议相比,MESI 协议减少了主存的事务数量。这极大改善了性能。[2]