缓存一致性问题

缓存更新策略

被动更新: 设置key过期的时间,让其自动失效。
主动更新: 更新DB的时候,同时更新缓存
一般业务都是主动更新和被动更新结合使用。

缓存更新实现

  1. 调用者调用。失败重试3次,再失败,扔给消息队列异步处理。消息队列从数据库取最新的更新,保证成功
  2. DB中间件监听DB变化,更新缓存
利用DB中间件监听DB变化(比如阿里的Canal中间件,点评的Puma),从而对缓存进行更新。
这种办法的一个好处就是:把缓存的更新逻辑,和业务逻辑解藕。业务只更新DB,缓存的更新被放在另外一个专门的系统里面
主动更新 问题
存在一个问题:你是先更新缓存,后更新DB;还是反过来?
下面分别分析以下2个场景,假设有2个线程,t1, t2:
(1) 先更新缓存,后更新DB。假设有如下的执行系列:

t1更新缓存;
t2读缓存,因为t1把缓存更新了,导致t2没读到。从db中读,然后更新缓存;
t1更新DB上述操作系列会导致缓存脏数据。 
但是这个不更新 缓存再读都是错的。只有等到下次更新才行。
但是更新操作总是比读操作少

(2)先更新DB,后更新缓存。假设有如下操作序列:

t1更新DBt2更新DBt2更新缓存;
t1更新缓存。
  • 1 上述操作系列会导致缓存脏数据。 但是这个不更新 缓存再读都是错的。只有等到下次更新才行。 但是更新操作总是比读操作少

  • 2 这个出现的情况概率小点。同时更新可以加乐观锁 再加上缓存过期失效策略,只会有部分时间不一致

  • 一句话,无论谁先谁后,只要更新缓存和更新DB不是原子的,就可能导致不一致。只是从实际业务来讲,一般缓存也都是保持“最终一致性“,而不是和DB的强一致性。

  • 并且一般建议先更新DB,再更新缓存,优先保证DB数据正确。

强一致

锁/分布式锁

缓存穿透

某个key,先查cache没查到,再查db也没有查到。 这种key的存在,会导致cache一直没办法命中,压力一直打在db上面。如果访问很高频,可能会压垮DB。

解决

- 当查询DB没查到时,往缓存中写入一个空值(缺省值),这样第2次再查,就不会打到DB上了。

缓存雪崩

缓存机器挂了,保证缓存的可用性 解决

  • 应用内也保持应用内缓存
  • 提高缓存的HA。比如缓存的主从复制。
  • 对DB的访问实行限流、降级

缓存并发

缓存过期后将尝试从后端数据库获取数据,这是一个看似合理的流程。但是,在高并发场景下,有可能多个请求并发的去从数据库获取数据, 对后端数据库造成极大的冲击,甚至导致 “雪崩”现象。此外,当某个缓存key在被更新时,同时也可能被大量请求在获取,

  • 解决 :锁/分布式锁 重试解决
类似“锁”的机制,在缓存更新或者过期的情况下,
先尝试获取到锁,当更新或者从数据库获取完成后再释放锁,其他的请求只需要牺牲一定的等待时间,即可直接从缓存中继续获取数据。

其他方法 不建议

由于数据库层面的读写并发,引发的数据库与缓存数据不一致的问题(本质是后发生的读请求先返回了),可能通过两个小的改动解决:
1)修改服务Service连接池,id取模选取服务连接,能够保证同一个数据的读写都落在同一个后端服务上
2)修改数据库DB连接池,id取模选取DB连接,能够保证同一个数据的读写在数据库层面是串行的
posted @ 2018-06-01 18:21  穿林度水  阅读(259)  评论(0编辑  收藏  举报