第3集Redis工作原理之双写一致性
什么是双写一致性问题?
在使用Redis作为MySQL的缓存时,我们经常会遇到双写一致性问题。所谓双写一致性,是指当数据需要同时写入数据库和缓存时,如何保证两者数据的一致性。
双写一致性的主要矛盾
双写一致性问题主要有两个核心矛盾:
- 更新完数据库后,是更新缓存还是删除缓存?
- 如果选择删除缓存,是先删缓存还是先更新数据库?
解决方案分析
方案1:缓存设置过期时间
设置缓存过期时间是保持最终一致性的基础方案,但无法保证强一致性。
- 原理:缓存过期后,再次获取缓存时会走数据库,获取到最新数据后再更新缓存
- 优点:实现简单,不需要额外的操作
- 缺点:在过期前可能会出现数据不一致的情况
方案2:先更新数据库,再更新缓存
这种做法实际上是不可取的,因为不能保证线程安全。
问题场景:
- 线程A更新了数据库,还没更新缓存
- 线程B更新了同一数据,并且更新了缓存
- 线程A再把缓存更新为自己的值
- 结果:从时效上看,B的更新应该覆盖A的,但实际上A覆盖了B的更新
业务角度:如果写操作频繁,会导致缓存频繁更新,性能浪费严重
方案3:先删除缓存,再更新数据库
这种方案可能导致脏读问题:
- 问题场景:
- 线程A进行写操作,删除缓存,还没更新数据库
- 线程B来查询数据,因为缓存被删除,去查数据库,得到旧值并更新到缓存
- 线程A将新值写入数据库
- 结果:缓存中是旧数据,数据库是新数据,产生不一致
方案4:延时双删策略
为了解决先删除缓存再更新数据库可能导致的脏读问题,引入延时双删策略:
- 先删除缓存
- 更新数据库
- 延时一段时间(确保读请求完成)
- 再次删除缓存
伪代码:
1 | // 1. 先删除缓存 |
- 延时时间:应该是读操作的耗时+几百毫秒,确保删除的是读操作产生的脏数据
- 缺点:延时会导致时间消耗,降低吞吐量
方案5:延时异步双删策略
为了解决延时导致的吞吐量问题,可以采用异步方式:
- 先删除缓存
- 更新数据库
- 新开一个线程,延时后再次删除缓存
伪代码:
1 | // 1. 先删除缓存 |
方案6:消息队列重试机制
如果第二次删除失败,可以通过消息队列建立重试机制:
- 将删除失败的key放入消息队列
- 单独编写一个消费者服务,不断尝试删除key直到成功
方案7:订阅Binlog删除缓存
通过订阅数据库的Binlog,获得需要操作的数据:
- 使用MySQL的Canal等工具订阅Binlog
- 在应用程序中编写方法接收订阅消息
- 根据消息删除对应的缓存
方案8:先更新数据库,再删除缓存(推荐方案)
Facebook等公司采用的策略是先更新数据库再删除缓存:
- 从缓存读取数据,没有则从数据库读取并更新缓存
- 更新数据时,先更新数据库,然后删除缓存
- 优点:发生不一致的概率极低
- 原因:要出现不一致,需要读操作比写操作慢,而实际上读操作通常比写操作快得多
可能的问题场景:
- 线程A查询数据,发现缓存没有,从数据库读取
- 线程B更新数据库,然后删除缓存
- 线程A将从数据库读取的旧值更新到缓存
- 结果:缓存是旧值,数据库是新值
解决方法:
- 如果要解决这个极低概率的问题,可以在线程A更新缓存时增加一个版本号检查
- 或者采用延时双删策略,在更新数据库后延时再删除一次缓存
各方案对比与选择
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
缓存过期 | 简单易实现 | 无法保证强一致性 | 对一致性要求不高的场景 |
先更新DB再更新缓存 | 直观 | 线程不安全,性能浪费 | 不推荐 |
先删缓存再更新DB | 避免频繁更新缓存 | 可能导致脏读 | 不推荐 |
延时双删 | 解决脏读问题 | 延时降低吞吐量 | 对一致性要求高的场景 |
异步延时双删 | 不影响主流程性能 | 实现复杂 | 高并发且要求一致性的场景 |
消息队列重试 | 保证最终一致性 | 需要额外的消息队列 | 对最终一致性有保障要求的场景 |
订阅Binlog | 可靠性高 | 实现复杂,依赖Binlog | 大型系统,对一致性要求高的场景 |
先更新DB再删缓存 | 不一致概率极低 | 理论上仍有不一致可能 | 大多数业务场景(推荐) |
最佳实践建议
- 一般场景:采用”先更新数据库,再删除缓存”的策略
- 高一致性要求:在上述策略基础上增加延时异步二次删除
- 超高并发场景:考虑引入消息队列或Binlog订阅机制
- 兜底方案:为所有缓存设置合理的过期时间,作为最终的一致性保障
总结
双写一致性是使用Redis作为MySQL缓存时必须面对的问题。没有完美的解决方案,需要根据业务场景和一致性要求选择合适的策略。在大多数场景下,”先更新数据库,再删除缓存”是相对最优的选择,同时设置合理的缓存过期时间作为兜底方案。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 1024bibi.com!
评论