缓存数据库更新策略

Cache Aside Pattern(旁路缓存模式)
对于读请求
先读cache,再读db
如果,cache hit,则直接返回数据
如果,cache miss,则访问db,并将数据set回缓存
对于写请求
 淘汰缓存,而不是更新缓存
 先操作数据库,再淘汰缓存
Cache Aside Pattern为什么建议淘汰缓存,而不是更新缓存?
答:如果更新缓存,在并发写时,可能出现数据不一致。
如果采用set缓存。
在1和2两个并发写发生时,由于无法保证时序,此时不管先操作缓存还是先操作数据库,都可能出现:
(1)请求1先操作数据库,请求2后操作数据库
(2)请求2先set了缓存,请求1后set了缓存
导致,数据库与缓存之间的数据不一致。
 
 所以,Cache Aside Pattern建议,delete缓存,而不是set缓存。
Cache Aside Pattern为什么建议先操作数据库,再操作缓存?
答:如果先操作缓存,在读写并发时,可能出现数据不一致。
架构师之路原文是:
如果先操作缓存。
在1和2并发读写发生时,由于无法保证时序,可能出现:
(1)写请求淘汰了缓存
(2)写请求操作了数据库(主从同步没有完成)
(3)读请求读了缓存(cache miss)
(4)读请求读了从库(读了一个旧数据)
(5)读请求set回缓存(set了一个旧数据)
(6)数据库主从同步完成
导致,数据库与缓存的数据不一致。
其实我认为就算没有主从同步这个问题,只有一台数据库,也会出现数据不一致的情况:
(1)写请求淘汰了缓存
(2)此时进来一个读请求,将旧的数据set进缓存
(3)写请求操作了数据库
这种情况的概率是很大的,因为大多数情况下的都是读多写少的场景。
 
 所以,Cache Aside Pattern建议,先操作数据库,再操作缓存。
Cache Aside Pattern方案存在什么问题?
 从并发角度
比如,一个是读操作,但是没有命中缓存(缓存刚好到期),就会到数据库中取数据。而此时来了一个写操作,写完数据库后,让缓存失效,然后之前的那个读操作再把老的数据放进去,所以会造成脏数据。
这个案例理论上会出现,但实际上出现的概率可能非常低,因为这个条件需要发生在读缓存时缓 存失效,而且有一个并发的写操作。实际上数据库的写操作会比读操作慢得多,而且还要锁表, 而读操作必需在写操作前进入数据库操作,又要晚于写操作更新缓存,所有这些条件都具备的概率并不大。
所以不能完全避免并发导致的数据不一致问题,只能从概率上保证。
 分布式事务角度
如果先操作数据库,再淘汰缓存,在原子性被破坏时:
(1)修改数据库成功了
(2)淘汰缓存失败了
导致,数据库与缓存的数据不一致。
但是
Cache Aside Pattern操作缓存失败,可以通过重试加打日志发现补救,也可以通过canal或者databus走kafka来失效缓存,基本能解决调缓存失败的情况。
 
 

posted @ 2018-12-09 19:45  老王子H  阅读(1131)  评论(0编辑  收藏  举报