MySQL主从集群与Redis缓存更新先后顺序,以及如何保证并发情况下Redis缓存被正确更新
最终一致性
记录一下主要解决方案
问题关键点在于数据库与缓存先更新谁?
对于数据读取以及缓存应用方案,通常是: 请求 -> 查缓存(命中返回) -> 查库(命中返回) -> 返回同时写缓存
所以实际有以下四种可能:
- 先更新缓存,再更新数据库
- 先删除缓存,再更新数据库
- 先更新数据库,再删除缓存
- 先更新数据库,再更新缓存
如果对缓存下手
先更新缓存,再更新数据库
数据库更新失败是要回滚的,意味着缓存也需要回滚,缓存维护工作是个问题,and,如果缓存中缓存的是程序运行结果,回滚将增加负担
先删除缓存,再更新数据库
这是主要是 脏数据
的问题, 可能存在以下时序:
数据库中此时有数据D1,缓存有R1
进程A -> 删除缓存R1 -> D1更新为D2 -> 等待后续进程访问重新读取D2,并设置缓存R1 = D2
进程B —> 读取R1失败 -> 读取DB得到D1 -> 设置缓存R1 = D1
此时,进程A更新的数据不会被写入缓存,缓存中仍然是脏数据
如果先对数据库下手
先更新数据库,再更新缓存
这是主要也是 脏数据
的问题, 可能存在以下时序:
进程A -> 更新DB,更新数据D1 -> 使用D1更新缓存R1
进程B -> 更新DB,更新数据D2 —> 使用D2更新缓存R1
此时,缓存中的结果还可信吗?缓存到了一个脏数据,缓存与库中记录不一致
先更新数据库,再删除缓存
这里有一个小概率情况,当select比update慢时,可能存在以下时序
缓存R1失效 -> 进程A查询DB,得到D1 -> 更新缓存R1 = D1
进程B更新DB,D修改为D2 —> 删除缓存R1
还是 脏数据
问题
这里要注意如果针对高并发情况,热点数据失效防止同时过多请求打到DB,可以访问DB前加分布式锁只允许一个进程访问数据库 在其他进程拿到锁以后,先尝试获取缓存
总结
分析几种缓存与数据库更新删除先后顺序可能出现的情况,可以得到结论是我们应该先对数据库进行更新,其实在主从数据库中,进程一般写master节点、读slave节点,两个节点之间同步也存在延迟,Redis缓存更新的情况更加繁杂
目前主要有两种解决方案
- 延迟双删
- 订阅主从库之间同步的binglog日志
延迟双删
即先更新数据库,再删除缓存
的拓展方案,先将缓存删除,再更新数据库,等待n时间后,再删除缓存
但是等待时间一般根据系统实际情况按经验而定,仍然有可能出现问题
根据binlog日志进行缓存更新
写master成功情况下,master会将数据通过binlog同步到从库,此时可以使用中间件(如阿里的canal)去订阅binlog日志(通过单线程订阅保证订阅事件顺序),通过日志中最新的内容更新Redis
注:Redis如果更新失败,可以将消息放到消息队列重复执行
强一致性
加锁做互斥并且写数据库和写缓存搞成一个事务,over
日后探讨