缓存与数据双写一致性问题
一般来说,执行更新操作时,我们会有两种选择:
- 先操作数据库,再操作缓存
- 先操作缓存,再操作数据库
这两个操作要么同时成功,要么同时失败。所以,这会演变成一个分布式事务的问题。
如果原子性被破坏,会有两种情况:
- 操作数据库成功,操作缓存失败
- 操作缓存成功,操作数据库失败
如果第一步已经失败了,我们直接返回Exception出去就好了,第二步根本不会执行。
先更新数据库,再更新缓存
正常情况:
- 先操作数据库,成功
- 在操作缓存,成功
如果原子性被破坏了:
- 第一步操作数据库成功,第二步操作缓存失败,会导致数据库中的数据是最新的,缓存中的数据是旧数据。
- 如果第一步操作数据库就失败了,可以直接返回错误(Exception),不会出现数据不一致。
删除缓存失败的解决策略:
- 将要删除的key放到消息队列中
- 自己去消费,消费失败就重试直至消费成功
先删除缓存,再更新数据库
正常情况:
- 删除缓存成功
- 更新数据库成功
如果原子性被破坏:
- 第一步删除缓存成功,第二步更新数据库失败,这个没有问题,数据库中的数据和缓存中的还是一致的。
- 第一步删除缓存失败的话,也是一样可以直接返回(Exception)错误,数据库中的数据和缓存中的还是一致的。
意外情况:
A删除缓存
B发现此时缓存没有,从数据库中读进来更新缓存
A更新了数据
此时缓存中的数据就是旧值了。
意外情况的解决策略:
- 用消息队列,将操作都发送到消息队列中的同一个 partiion 中,保证消费消息的顺序性。
![](https://i-blog.csdnimg.cn/blog_migrate/2eb2d6e4ddeaa2c86d1551ab9914ff50.png)
- 在删除缓存时候,用排它读锁给这条数据锁住,等此线程更新完之后再解锁 (个人见解,因为考虑到意外情况嘛,出现的频率肯定不是很高,在这引入消息队列系统会变得很复杂)
其他保障数据一致的方案与资料
可以用 databus 或者阿里的 canal监听binlog 进行更新。