缓存更新策略 - 一致性

[TOC]

参考

Cache Aside Pattern

究竟先操作缓存,还是数据库?

缓存更新的套路

使用缓存的正确姿势

缓存的正确使用方式,你都会了吗?

1. 概要

缓存服务和数据服务(如数据库)是独立的系统,更新的时候,无法做到原子性地操作两个服务,即有两步操作来更新缓存服务和数据服务的数据。因此在并发读写以及第二步操作异常时,会出现各种问题。

此处说的第二步操作,要么是操作缓存服务,要么是操作数据服务,根据具体的实现方式而定。

2. 缓存使用的误区

  1. 服务与服务之间不要通过缓存传递数据

  2. 如果缓存挂掉,可能导致雪崩,此时要做高可用缓存,或者水平切分

  3. 调用方不宜再单独使用缓存存储服务底层的数据,容易出现数据不一致,以及反向依赖

  4. 不同服务,缓存实例要做垂直拆分

3. 常见更新模式

3.1. cache aside

同时更新缓存和数据库
这是最常用的pattern了。其具体逻辑如下:

  • 数据查询:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
  • 数据更新:先把数据存到数据库中,成功后,再让缓存失效。
    此处,更新的时候是先数据库,后缓存,也有另一种思路,先缓存,后数据库。两种思路详见后文分析。

3.1.1. 先数据库后缓存

这是最常用最常用的pattern。
分析异常情况如下:

  • \color{blue}{并发读写}
    • 一个是查询请求,一个是更新请求的并发。更新请求在写完数据库之后,让缓存失效之前,查询请求到来,此时,缓存依然有效,所以,并发的查询请求拿的是没有更新的数据,但是,随后的更新请求会马上让缓存的失效了,在这之后的查询请求再把数据从数据库中取出来,放入到缓存。\color{green}{不会出现不一致。}
    • 同样是一个查询请求,一个更新请求的并发。查询请求 cache miss,读了数据库,在将数据放到缓存之前,更新请求到来,更新了数据库内容且让缓存失效,此时查询请求再将之前读的老数据放入到缓存。这样也会导致缓存和数据不一致。\color{red}{出现不一致,只是概率很小。} 为了避免这种情况,可以采取延迟失效,同时读请求回填 cache 的时候,如果遇到 key 存在,则不更新,只能等待超时失效之后,读请求回填的 cache 才可以更新;也可以考虑延迟双删,缓存失效后,等待1s 再失效一次缓存。
  • \color{red}{第二步操作异常}:如果是更新请求在写完数据库,让缓存失效之前,更新请求发起方异常(如宕机),此时数据库已经更新了,但是缓存一直没有更新。导致查询请求从缓存获取的数据一直都是老数据。为了规避第二步操作异常,有两种方法:1. 考虑在缓存上加上失效时间,但是这和周期性的更新缓存类似,还是会对系统性能还是有较大的影响;2. 使用可靠的消息队列记录(如 binlog)更改,然后消费消息进行缓存失效

这是标准的design pattern,包括Facebook的论文《Scaling Memcache at Facebook》也使用了这个策略。

3.1.2. 先缓存后数据库

分析如下:

  • \color{red}{并发读写}:一个是查询请求,一个是更新请求的并发。在更新请求让缓存失效之后,写数据库之前,查询操作到来,此时,会从数据库读到老数据进而写回到缓存,等更新请求写完数据库,\color{red}{就会出现不一致。}

  • \color{red}{第二步操作异常}:如果是更新请求将缓存失效后,在写数据库前,更新操作发起方异常(如宕机),此时仅仅是 cache miss,\color{green}{不会导致不一致。}

针对第二种方案,我们可以采取以下两种方案规避并发读写的问题:

  1. 在使缓存失效的时候,不是立即失效,而是采取延迟失效(比如说设置缓存失效时间为1s),保证在数据库更新之前,读请求依旧能够命中cache,进而不会出现读请求更新 cache 的情况。这个方案应该是最佳方案。
  2. 延迟双删。第一次缓存失效且数据库操作完成之后,延迟再失效一次缓存。

3.1.3. 更新缓存还是失效缓存

为什么不是更新缓存,而是失效缓存?你可以看一下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?》,主要是\color{red}{怕两个并发的更新操作导致脏数据.}

3.1.3. 结论

个人觉得,先缓存后数据库,配合延迟失效的方案最好。欢迎读者讨论。

3.2. Read/Write Through Pattern

先更新缓存,缓存负责同步更新数据库。

在上面的 Cache Aside 更新模式中,应用代码需要维护两个数据存储,一个是缓存(Cache),一个是数据库(Repository)。而在Read/Write Through 更新模式中,应用程序只需要维护缓存,数据库的维护工作由缓存代理了。


write/read through流程图

3.2.1. Read Through

Read Through 模式就是在查询操作中更新缓存,也就是说,当缓存失效的时候,Cache Aside 模式是由调用方负责把数据加载入缓存,而 Read Through 则用缓存服务自己来加载。

3.2.2. Write Through

Write Through 模式和 Read Through 相仿,不过是在更新数据时发生。当有数据更新的时候,如果没有命中缓存,直接更新数据库,然后返回。如果命中了缓存,则更新缓存,然后由缓存自己更新数据库(这是一个同步操作)。

3.3. Write Behind Caching Pattern

先更新缓存,缓存定时异步更新数据库。

Write Behind Caching 更新模式就是在更新数据的时候,只更新缓存,不更新数据库,而我们的缓存会异步地批量更新数据库。这个设计的好处就是直接操作内存速度快。因为异步,Write Behind Caching 更新模式还可以合并对同一个数据的多次操作到数据库,所以性能的提高是相当可观的。

但其带来的问题是,数据不是强一致性的,而且可能会丢失。另外,Write Behind Caching 更新模式实现逻辑比较复杂,因为它需要确认有哪些数据是被更新了的,哪些数据需要刷到持久层上。只有在缓存需要失效的时候,才会把它真正持久起来。

write behind 流程图

3.4. 总结

三种缓存模式的优缺点:

  • Cache Aside 更新模式实现起来比较简单,但是需要维护两个数据存储,一个是缓存(Cache),一个是数据库(Repository)。
  • Read/Write Through 更新模式只需要维护一个数据存储(缓存),但是实现起来要复杂一些。
  • Write Behind Caching 更新模式和Read/Write Through 更新模式类似,区别是Write Behind Caching 更新模式的数据持久化操作是异步的,但是Read/Write Through 更新模式的数据持久化操作是同步的。优点是直接操作内存速度快,多次操作可以合并持久化到数据库。缺点是数据可能会丢失,例如系统断电等。

缓存是通过牺牲强一致性来提高性能的。所以使用缓存提升性能,就是会有数据更新的延迟。这需要我们在设计时结合业务仔细思考是否适合用缓存。然后缓存一定要设置过期时间,这个时间太短太长都不好,太短的话请求可能会比较多的落到数据库上,这也意味着失去了缓存的优势。太长的话缓存中的脏数据会使系统长时间处于一个延迟的状态,而且系统中长时间没有人访问的数据一直存在内存中不过期,浪费内存。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345