缓存与数据库双写数据一致性问题
本质上来说,双写一致性问题是因为更新数据库和更新缓存不是原子操作,如果要做到强一致性,只能加分布式锁。不然就是做取舍问题。
缓存是用在:不经常变动,但是又经常查询,而且发生脏读不敏感的场景下使用
缓存+数据库读写模式:
- cache aside pattern
- 读的时候,先读缓存,缓存没有的话就读数据库,读到的数据库的值再存入缓存。
- 数据更新的时候,更新数据库,删除缓存。
- Read-Through/Write-Through(读写穿透)
- 读写通过Cache Provider封装的api,Cache Provider进行缓存的读取和数据库的读取&更新缓存,写的时候也是通过Provider进行。
- Write-behind(异步缓存写入)
- 通过Cache Provider进行读写访问,Cache Provider在缓存层进行更新,不同步更新数据库,而是通过间隔一段时间批量异步更新数据库,如linux的os cache实现,写入之后定时进行fsync才会真正刷入磁盘
如何解决双写数据一致性问题
0. 读的时候,先读缓存,缓存没有的话就读数据库,读到的数据库的值再存入缓存,这种模式有极低的概率会导致不一致,可以给缓存设置一个过期时间,避免极少数key一直不一致
- 强一致性情况下使用分布式事务对读写进行控制,但会降低并发量,没有什么意义。
- 弱一致性情况下使用延迟双删,更新数据库之前删除缓存,更新之后再延迟删除缓存,延迟的时间可以是读业务逻辑的耗时+几百毫秒
- 高并发读写的情况下,在一个修改请求删除了缓存之后,准备进行数据库更新,这时候有一个读请求进来,发现缓存为空,然后去数据库获取到旧值,保存到缓存中,然后修改请求修改数据库完成,这时候出现不一致问题
这时候可以考虑把缓存更新和读取操作进行异步串行化,将读写请求根据唯一标识进行路由,发送到一个内部队列中,
- 删除缓存重试机制
- 读取biglog异步删除缓存
- 写-延迟删缓存
- 消息队列异步更新缓存,注意带上时间戳避免并发写时顺序与写DB不一致
整体来说还是需要结合实际项目,一致性和并发不能兼顾,具体情况具体分析了,如果是需要强一致性,其实缓存也就没有意义了,直接读库就行了…