为了账号安全,请及时绑定邮箱和手机立即绑定

大行缓存更新之道

标签:
职场生活

好些人在写更新缓存时,先删除缓存,然后再更新数据库,而后续的操作会把数据再装载的缓存中。
然而,这个逻辑是错误的。试想,两个并发操作,一个是更新操作,另一个是查询操作,更新操作删除缓存后,查询操作没有命中缓存,先把老数据读出来后放到缓存中,然后更新操作更新了数据库。于是,在缓存中的数据还是老的数据,导致缓存中的数据是脏的,而且还一直这样脏下去了。

不知道为什么这么多人用的都是这个逻辑,总结一下几个缓存更新的Design Pattern。

这里,我们先不讨论更新缓存和更新数据这两个事是一个事务的事,或是会有失败的可能,我们先假设更新数据库和更新缓存都可以成功的情况

更新缓存的的Design Pattern有四种

1  Cache Aside Pattern

最常用的pattern。具体逻辑如下:

  • 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中

  • 命中:应用程序从cache中取数据,取到后返回

  • 更新:先把数据存到数据库中,成功后,再让缓存失效

    Cache-Aside-Design-Pattern-Flow-Diagram


    Updating Data using the Cache-Aside Pattern - Flow Diagram

注意,我们的更新是先更新数据库,成功后,让缓存失效

一个查询操作,一个更新操作的并发
首先,没有了删除cache数据的操作,而是先更新数据库中的数据,此时,缓存依然有效,所以,并发的查询操作拿的是没有更新的数据,但是,更新操作马上让缓存的失效了,后续的查询操作再把数据从数据库中拉出来。而不会像文章开头的那个逻辑产生的问题,后续的查询操作一直都在取老的数据。

这是标准的design pattern,包括Facebook的论文《Scaling Memcache at Facebook》也使用了这个策略
为什么不是写完数据库后更新缓存?可以看一下Quora上的这个问答《Why does Facebook use delete to remove the key-value pair in Memcached instead of updating the Memcached during write request to the backend?》,主要是怕两个并发的写操作导致脏数据。

那么,是不是Cache Aside这个就不会有并发问题了?
不是的,比如,一个是读操作,但是没有命中缓存,然后就到数据库中取数据,此时来了一个写操作,写完数据库后,让缓存失效,然后,之前的那个读操作再把老的数据放进去,所以,会造成脏数据。

这个情形理论上会出现,不过,实际上出现的概率可能非常低,因为需要发生在读缓存时缓存失效,而且并发着有一个写操作。
而实际上数据库的写操作会比读操作慢得多,而且还要锁表,而读操作必需在写操作前进入数据库操作,而又要晚于写操作更新缓存,所有的这些条件都具备的概率基本并不大

这也就是Quora上的那个答案里说的,要么通过2PC或是Paxos协议保证一致性,要么就是拼命的降低并发时脏数据的概率,而Facebook使用了这个降低概率的玩法,因为2PC太慢,而Paxos太复杂。当然,最好还是为缓存设置上过期时间。

2 Read/Write Through Pattern

上面的Cache Aside,应用代码需要维护两个数据存储,一个是缓存,一个是数据库,应用程序比较啰嗦。
Read/Write Through是把更新数据库的操作由缓存自己代理,所以,对于应用层来说,就简单很多。
可理解为,应用认为后端就是一个单一的存储,而存储自己维护自己的Cache。

2.1  Read Through

Read Through 就是在查询操作中更新缓存,也就是说,当缓存失效的时候(过期或LRU换出)

  • Cache Aside是由调用方负责把数据加载入缓存

  • Read Through则用缓存服务自己来加载,从而对应用方是透明的

2.2  Write Through

和Read Through相仿,不过是在更新数据时发生
当有数据更新时

  • 如果没有命中缓存,直接更新数据库,然后返回

  • 如果命中了缓存,则更新缓存,然后再由Cache自己更新数据库(这是一个同步操作)

下图中的Memory可以理解为就是我们例子里的数据库


A write-through cache with no-write allocation

3 Write Behind Caching Pattern

Write Behind 又叫 Write Back
在更新数据的时候,只更新缓存,不更新数据库,而我们的缓存会异步地批量更新数据库
这个设计的好处就是

  • 让数据的I/O操作飞快无比(因为直接操作内存嘛 )

  • 因为异步,write back还可以合并对同一个数据的多次操作,所以性能的提高是相当可观的

但是,其带来的问题是,数据不是强一致性的,而且可能会丢失(我们知道Unix/Linux非正常关机会导致数据丢失,就是因为这个事)。

另外,Write Back实现逻辑比较复杂,因为他需要track哪些数据是被更新的,需要刷到持久层上。
操作系统的write back会在仅当这个cache需要失效的时候,才会被真正持久化,比如,内存不够了,或是进程退出了等情况,这又叫lazy write


A write-back cache with write allocation



作者:芥末无疆sss
链接:https://www.jianshu.com/p/c58e929d4a40
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消