Redis如何保障缓存与数据库的数据一致性问题?

用张别人的图来总结下缓存与数据库不一致的情况,大家可以先看,也可以直接看下面文字

一.最经典的数据库加缓存的双写双删模式

1.1 Cache Aside Pattern概念以及读写逻辑

(1)读的时候,先读缓存,缓存没有的话,那么就读数据库,然后取出数据后放入缓存,同时返回响应
(2)更新的时候,先删除缓存,然后再更新数据库

1.22、为什么是删除缓存,而不是更新缓存呢?

原因很简单,很多时候,复杂点的缓存的场景,因为缓存有的时候不简单是数据库中直接取出来的值,可能需要比较复杂的计算,甚至进行很多网络请求以及DB请求(比如我们有个缓存就是查微信的公共库以及我们自己的私有库联合组成一个缓存),这种更新缓存的代价很高的,但是呢我们更新完了缓存这个缓存,这个缓存也不一定立马就有人用,可能我更新了很多次数据库更新了很多次缓存都没人访问,这就导致了我服务器做了很多无用的计算

二. 高并发场景下的缓存+数据库双写不一致问题分析与解决方案设计

这里围绕和结合实时性较高的库存服务,把数据库与缓存双写不一致问题以及其解决方案,给大家讲解一下.

我们有两个操作顺序可以选择,其中都存在各种双鞋不一致情况,具体讨论讨论

  • 更新数据先删除缓存,再更新数据库
  • 更新数据先更新数据库,再删除缓存

2.1先删除缓存再更新数据库方式

2.1.1 上面说的最经典的方式有什么缓存不一致的问题?解决方案是什么?

问题:如果我们的方案是先修改数据库库存,再删除缓存,那么如果删除缓存失败了,那么会导致数据库中是新数据,缓存中是旧数据,数据出现不一致

解决思路:
先删除缓存,再修改数据库,如果删除缓存成功了,如果修改数据库失败了,那么数据库中是旧数据,缓存中是空的,那么数据不会不一致,因为读的时候缓存没有,则读数据库中旧数据,然后更新到缓存中

注意这里无并发读写没问题,但是并发情况下依然会有问题,我们继续往下看

2..22 上面第一个解决方案在并发下还是有问题

如果先删除缓存再删除数据库可能存在这种情况

  1. A服务删除缓存成功
  2. B请求来了读旧数据库存
  3. A更新新的库存成功

这样依然是数据库和缓存的库存不一致了

2.3 如何允许短暂的不一致,我们可以用什么思路来做?

2.3.1 基于MQ的分布式事务实现最终一致性
2.3.2 基于binlog监听实现
2.3.3 延迟双删 (比上面稍微优点的一点在于这里不需要印入MQ)

延时双删
延时双删的方案的思路是,为了避免更新数据库的时候,其他线程从缓存中读取不到数据,就在更新完数据库之后,再 Sleep 一段时间,然后再次删除缓存。
Sleep 的时间要对业务读写缓存的时间做出评估,Sleep 时间大于读写缓存的时间即可。
流程如下:
线程1删除缓存,然后去更新数据库。
线程2来读缓存,发现缓存已经被删除,所以直接从数据库中读取,这时候由于线程1还没有更新完成,所以读到的是旧值,然后把旧值写入缓存。
线程1,根据估算的时间,Sleep,由于 Sleep 的时间大于线程2读数据+写缓存的时间,所以缓存被再次删除。

如果还有其他线程来读取缓存的话,就会再次从数据库中读取到最新值。

高并发下又要求强一致性的解决思路:将统一商品的请求进行串行化
总结了一张图大家可以看看

2.3上面高并发的场景下,该解决方案要注意的问题

2.3.1读请求长时阻塞

由于读请求进行了非常轻度的异步化,所以一定要注意读超时的问题,每个读请求必须在超时时间范围内返回

该解决方案,最大的风险点在于说,可能数据更新很频繁,导致队列中积压了大量更新操作在里面然后读请求会发生大量的超时,最后导致大量的请求直接走数据库

务必通过一些模拟真实的测试,看看更新数据的频繁是怎样的

因为一个队列中,可能会积压针对多个数据项的更新操作,因此需要根据自己的业务情况进行测试,可能需要部署多个服务,每个服务分摊一些数据的更新操作

如果一个内存队列里居然会挤压100个商品的库存修改操作,每隔库存修改操作要耗费10ms区完成,那么最后一个商品的读请求,可能等待10 * 100 = 1000ms = 1s后,才能得到数据

这个时候就导致读请求的长时阻塞

一定要做根据实际业务系统的运行情况,去进行一些压力测试,和模拟线上环境,去看看最繁忙的时候,内存队列可能会挤压多少更新操作,可能会导致最后一个更新操作对应的读请求,会hang多少时间,如果读请求在200ms返回,如果你计算过后,哪怕是最繁忙的时候,积压10个更新操作,最多等待200ms,那还可以的

如果一个内存队列可能积压的更新操作特别多,那么你就要加机器,让每个机器上部署的服务实例处理更少的数据,那么每个内存队列中积压的更新操作就会越少

其实根据之前的项目经验,一般来说数据的写频率是很低的,因此实际上正常来说,在队列中积压的更新操作应该是很少的

针对读高并发,读缓存架构的项目,一般写请求相对读来说,是非常非常少的,每秒的QPS能到几百就不错了

一秒,500的写操作,5份,每200ms,就100个写操作

单机器,20个内存队列,每个内存队列,可能就积压5个写操作,每个写操作性能测试后,一般在20ms左右就完成

那么针对每个内存队列中的数据的读请求,也就最多hang一会儿,200ms以内肯定能返回了

写QPS扩大10倍,但是经过刚才的测算,就知道,单机支撑写QPS几百没问题,那么就扩容机器,扩容10倍的机器,10台机器,每个机器20个队列,200个队列

大部分的情况下,应该是这样的,大量的读请求过来,都是直接走缓存取到数据的

少量情况下,可能遇到读跟数据更新冲突的情况,如上所述,那么此时更新操作如果先入队列,之后可能会瞬间来了对这个数据大量的读请求,但是因为做了去重的优化,所以也就一个更新缓存的操作跟在它后面

等数据更新完了,读请求触发的缓存更新操作也完成,然后临时等待的读请求全部可以读到缓存中的数据

3.2 读请求并发量过高

必须做好压力测试,确保恰巧碰上上述情况的时候,还有一个风险,就是突然间大量读请求会在几十毫秒的延时hang在服务上,看服务能不能抗的住,需要多少机器才能抗住最大的极限情况的峰值

但是因为并不是所有的数据都在同一时间更新,缓存也不会同一时间失效,所以每次可能也就是少数数据的缓存失效了,然后那些数据对应的读请求过来,并发量应该也不会特别大

按99:1的比例计算读和写的请求,每秒5万的读QPS,可能只有500次更新操作

如果一秒有500的写QPS,那么要测算好,可能写操作影响的数据有500条,这500条数据在缓存中失效后,可能导致多少读请求,发送读请求到库存服务来,要求更新缓存,这些读请求每个会hang多长时间?

如果我们写读比例是1:20,每秒更新500条数据,这500秒数据对应的读请求,会有20 * 500 = 1万,1万个读请求全部hang在库存服务上,就死定了

3.3 多服务实例部署的请求路由一致性问题

可能这个服务部署了多个实例,那么必须保证,同一个商品id(我们路由到queue的规则),执行数据更新库存操作,以及执行缓存更新操作的请求,都通过nginx服务器路由到相同的服务实例上(这个要改nginx的hash路由规则)

如果一个商品的库存更新操作在A服务器的queue里,他的读路由到另一个服务器的队列里去了,这他娘的还串行化个屁。

3.4 热点商品的路由问题,导致请求的倾斜

万一某个商品的读写请求特别高,全部打到相同的机器的相同的队列里面去了,可能造成某台机器的压力过大

其实只有在商品数据更新的时候才会清空缓存,然后才会导致读写并发,所以更新频率不是太高的话,这个问题的影响并不是特别大,但是的确可能某些机器的负载会高一些,需要注意。

3.5 .串行化缺点

一般来说,就是如果你的系统不是严格要求缓存+数据库必须一致性的话,缓存可以稍微的跟数据库偶尔有不一致的情况,最好不要做这个方案:读请求和写请求串行化,串到一个内存队列里去,这样就可以保证一定不会出现不一致的情况。

但是呢:串行化之后,就会导致系统的吞吐量会大幅度的降低,用比正常情况下多几倍的机器去支撑线上的请求。

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

推荐阅读更多精彩内容