缓存和DB中数据一致性

缓存和DB中数据一致性

淘汰还是更新

一般来说,是淘汰

  • 一般来说,修改的成本会高于淘汰的成本
    • 修改的话,假如存的是json字符串,需要先将数据反序列化,然后修改数据,然后序列化,再存入redis。修改的代价高,但是少一次cache miss
    • 淘汰的话,就将数据置为无效,但是多一次cache miss
  • 修改缓存,在并发写的时候,可能出现数据不一致
    • 比如说请求1 先写数据库,然后请求2写数据库
    • 接着请求2更新缓存,请求1更新缓存
    • 这就造成数据库是请求2的修改结果,缓存是请求1的修改结果
  • 一般的模式为:
    • 更新了数据,就直接淘汰掉缓存

顺序问题

是先操作缓存,还是先操作DB?

先操作缓存,再操作DB

先淘汰缓存成功,后更新DB失败(比如说服务挂了),不会造成不一致。

但是缓存淘汰了以后,主库还没有同步到从库,又有一个读请求,把旧的数据读到缓存,也会造成不一致。

这种情况下不一致概率是比较高的,因为一般情况下读请求远远高于写请求,当淘汰了缓存之后,在更新DB之前很有可能有读请求把从库的旧数据读到缓存中,从而造成不一致。

不过对于这种情况,有以下两种办法:

  • 给缓存设置过期时间,能达到最终一致性
  • 监听主库bin log,当主从同步完成,再淘汰一次缓存

但是这样子代价就比较高了,架构变得复杂。

先操作DB,后操作缓存

先更新DB,后更新缓存。
假如更新完DB后,服务挂了,没有更新缓存,缓存过期后,经历一次缓存miss,那么数据将达到最终一致。

DB主从延迟导致的缓存不一致

背景

缓存与数据库不一致

image-20191108231125166

如上图,发生的场景也是,写后立刻读:

(1+2)先一个写请求,淘汰缓存,写数据库

(3+4+5)接着立刻一个读请求,读缓存,cache miss,读从库,写缓存放入数据,以便后续的读能够cache hit(主从同步没有完成,缓存中放入了旧数据)

(6)最后,主从同步完成

导致的结果是:旧数据放入缓存,即使主从同步完成,后续仍然会从缓存一直读取到旧数据。

可以看到,加入缓存后,导致的不一致影响时间会很长,并且最终也不会达到一致。

方案

image-20191108231456000

如上图所述,在并发读写导致缓存中读入了脏数据之后:

(6)主从同步

(7)通过工具订阅从库的binlog,这里能够最准确的知道,从库数据同步完成的时间

画外音:本图画的订阅工具是DTS,也可以是cannal订阅和分析binlog

(8)从库执行完写操作,向缓存再次发起删除,淘汰这段时间内可能写入缓存的旧数据

(这样子还是在短时间内可能存在缓存和DB不一致,但是能达到最终一致性)

Cache Aside Pattern

Cache Aside Pattern是缓存经典实践方式,分为读实践、写实践。

对于读请求

  • 先读缓存
  • 缓存命中,直接返回数据
  • 缓存未命中,则查询DB
  • 将数据set到内存

对于写请求

  • 先更新数据库
  • 后淘汰缓存

总结

  • 先更新DB,再更新缓存
  • 尽量淘汰缓存
  • 可以采用纯内存数据库,然后异步更新到DB(要求高可用)

参考

缓存一致性

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值