数据库和缓存一致性问题
1、假设操作都能成功:
写:先更新数据库,后删除缓存
读:先读缓存,如果不存在读数据库,再写缓存
只要操作都成功,大部分时间可以做到缓存一致
除一种情况,2 个线程并发读写数据:
1、缓存中 X 不存在(数据库 X = 1)
2、线程 A 读取数据库,得到旧值(X = 1)
3、线程 B 更新数据库(X = 2)
4、线程 B删除缓存
5、线程 A 将旧值写入缓存(X = 1)
最终 X 的值在缓存中是 1(旧值),在数据库中是 2(新值),发生不一致。
其实概率「很低」,这是因为它必须满足 3 个条件:
1、缓存刚好已失效
2、读请求 + 写请求并发
3、更新数据库 + 删除缓存的时间(步骤 3-4),要比读数据库 + 写缓存时间短(步骤 2 和 5)
仔细想一下,条件 3 发生的概率其实是非常低的。因为写数据库一般会先「加锁」,所以写数据库,通常是要比读数据库的时间更长的。
2、假设有操作失败
程序在执行过程中发生异常,最简单的解决办法:异步重试。
其实就是把重试请求写到「消息队列」中,然后由专门的消费者来重试,直到成功。
或者订阅数据库变更日志,再操作缓存。
在「先更新数据库,再删除缓存」方案下,「读写分离 + 主从库延迟」也会导致缓存和数据库不一致,缓解此问题的方案是「延迟双删」,发送「延迟消息」到队列中,延迟删除缓存,同时也要控制主从库延迟,尽可能降低不一致发生的概率。
但是,这个时间在分布式和高并发场景下,其实是很难评估的。
很多时候,我们都是凭借经验大致估算这个延迟时间,例如延迟 1-5s,只能尽可能地降低不一致的概率。