Redis缓存与数据库数据一致性

Redis由于将数据保存在内存中,访问速度远远大于基于磁盘的数据库(如MySQL)。但是由于其容量有限,不开启持久化时断电数据容易丢失(开启持久化影响性能)等特点, 因此常常作为数据库的缓存来使用。Redis也主要适合于读多写少,且对一致性要求不是特别高的场景,这是使用Redis的前提。

缓存更新策略

由于引入缓存,数据就会分散在缓存和数据库两处不同的数据源,当数据更新时,事实上很难做到数据一致,除非采用强一致性方案,这里不在进行讨论。关于数据的更新,主要有以下4种模式, 其中Read Through和Write Through放在一起讨论:

  • Cache Aside
  • Read Through
  • Write Through
  • Write Back

这四种模式的主要区别在于最新数据在缓存中还是数据库中,由谁进行更新

模式 最新数据在哪里 由谁更新
Cache Aside 数据库 应用程序更新缓存
Read Through/Write Through 缓存服务更新数据库
Write Back 缓存 缓存服务更新数据库

下面分别对这四种模式进行阐述,为保证行文连贯,先假设更新数据库以及缓存都会事务成功,由于某一种更新导致的不一致性在后续章节讨论

1. Cache Aside

Cache Aside顾名思义,就是缓存“靠边站”,只是在访问数据库的主流程上帮个忙,最新的数据还是以数据库为主

Cache Aside主要有三点:

  1. 命中:应用程序从cache中取数据,取到后返回。
  2. 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
  3. 更新:先把数据存到数据库中,成功后,应用程序再让缓存失效。

至于为什么是先更新数据库,再让缓存失效, 而不是直接更新缓存,主要是为了保证在并发的情况下,尽可能降低数据不一致出现的概率,具体参考附录,在大概率的情况下先更新数据库再失效缓存能够保证数据一致,也是业界推荐的处理方式,包括Facebook的论文《Scaling Memcache at Facebook》也使用了这个策略。


图片来源于https://coolshell.cn/articles/17416.html

2. Read Through与Write Through

Cache Aside 对缓存以及数据库的更新逻辑是由应用程序去控制的,很显然这是一个很复杂的过程。Write/Read Through对调用方而言,缓存是作为整个的数据存储,而不用关心缓存后面的数据库,数据库的更新则是由缓存统一进行管理,对调用方而言只需要和缓存进行交互,整体过程是透明的。可以理解为,应用认为后端就是一个单一的存储,而存储自己维护自己的Cache。如下图,图片来源于Cache (computing)

  • Read Through
    查询操作中,当缓存失效的时(过期或LRU换出),由缓存服务负责从数据库中加载数据到缓存中,对应用方是透明的。
  • Write Through
    更新数据时,如果没有命中缓存,直接更新数据库,然后返回。
    如果命中了缓存,则更新缓存,然后再由缓存服务自己更新数据库(这是一个同步操作)

3. Write Back

这种模式是当数据更新的时候直接更新缓存数据,然后建立异步任务去更新数据库。这种异步方式请求响应会很快,系统的吞吐量会明显提升。但是,因为是异步更新数据库,数据一致性的保障就会变弱,如果更新数据库失败则会永远的造成系统脏数据,需要很精细设计系统重试的策略,另外如果异步服务宕机的话,还要考虑更新的数据如何持久化,服务重启后能够迅速恢复。在更新数据库时,由于并发多任务的存在,还需要考虑并发写是否会造成脏数据的问题,就需要追溯每次更新数据的时序。使用这种模式需要考虑的细节会有很多,设计出一套好的方案是件很不容易的事情。

图片来源于https://en.wikipedia.org/wiki/Cache_(computing)

数据不一致的原因

1. 数据不一致的原因

  1. 逻辑失败造成的数据不一致
    因为异步读写请求在并发情况下的操作时序导致的数据不一致,称之为”逻辑失败“。解决这种因为并发时序导致的问题,核心的解决思想是将异步操作进行串行化。
  2. 物理失败造成的数据不一致
    在Cache Aside 模式中先更新数据库再删除缓存以及异步双删策略等等,如果删除缓存失败时都出现数据不一致的情况。出于性能的考虑,数据库及缓存的操作不会放在一个事务中,因为缓存操作失败,导致的数据不一致称之为“物理失败”。大多数情况物理失败的情况会重用重试的方式进行解决。

2. 数据最终一致性解决方案:

在绝大部分业务场景中,追求的是最终一致性,针对物理失败造成的数据不一致常用的方案有:消费消息异步删除缓存以及订阅Binlog的方式,针对逻辑失败造成的数据不一致常用的方案有:队列异步操作同步化。
消费消息异步删除缓存
流程如下图所示,主要包括:

  1. 应用程序更新数据库数据;
  2. 删除缓存
  3. 如果缓存删除失败,将删除失败的key 发送到消息队列
  4. 应用程序自己消费消息,获得需要删除的key
  5. 继续重试删除操作,直到成功

订阅binlog
消费消息异步删除缓存有一个缺点,对业务线代码有大量的侵入,所以引出了本方案, 主要流程如下:

  1. 应用程序更新数据库
  2. 通过canal 订阅数据库的binlog
  3. 数据更新服务解析binlog
  4. 根据解析的binlog更新缓存
  5. 对于更新失败的,将失败的key发送到消息队列
  6. 缓存服务订阅消息队列, 重试

总结:

无论是4种更新策略,还是缓存的最终一致性方案,Redis缓存与数据库都会有短时间或者小概率的不一致的风险, 这又回到了开篇Redis适合使用的场景,读多写少, 对一致性要求不高,如果是一致性要求特别高的情况,比如交易场景,则只能使用数据库了。

附录

1. 先更新数据库再失效缓存

  • 数据库读请求比写请求快,出现数据不一致概率极低
    1. 读请求时,缓存刚好失效(图中1、2)
    2. 读请求从数据库中读取数据并更新缓存期间(图中3、4、9、10),正好有写请求更新数据库并使缓存失效(图中5、6、7、8), 且读数据库比写数据库慢
  • 可以通过异步双删的策略以及过期失效的方式来避免这种不一致
先更新数据库再失效缓存出错情况(概率小)

2. 先失效缓存再更新数据库

  • 数据库读请求比写请求快,容易出现数据不一致
    写请求失效缓存并更新数据库期间(图中1、2、7、8),读请求读取数据库旧值并更新缓存(图中3、4、5、6)
  • 通过延时双删(影响吞吐率),异步双删降低不一致的影响
先失效缓存再更新数据库数据不一致情况(概率大)

3. 先更新缓存再更新数据库与先更新数据库再更新缓存

  • 并发写容易写覆盖造成脏数据问题
    具体如如下两图所示
  • 双写不同数据源容易造成数据不一致
    同时写数据库以及缓存数据,任何一个更新失败都会造成数据不一致,即物理失败。另外事务都成功,无论是先更新缓存还是再更新数据库,还是先更新数据库再更新缓存,这两种情况在并发的情况下也很容易出现双写不成功。
  • 违背数据懒加载,避免不必要的计算消耗(这点还好)
    如果有些缓存值是需要经过复杂的计算才能得出,所以如果每次更新数据的时候都更新缓存,但是后续在一段时间内并没有读取该缓存数据,这样就白白浪费了大量的计算性能,完全可以后续由读请求的时候,再去计算即可,这样更符合数据懒加载,降低计算开销。


    先更新数据库再更新缓存数据不一致情况(概率大)
先更新缓存再更新数据库数据不一致情况(概率大)

参考:

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

推荐阅读更多精彩内容