缓存和数据库数据的一致性


如何保证缓存与数据库的双写一致性?

概念:

使用缓存的时候,一般情况下要求缓存与数据库中的数据保持一致
在写入数据,对数据进行修改的时候,需要对缓存中的数据和数据库中的数据进行一起修改

使用缓存策略(Cache Aside Pattern)

读取数据:
首先尝试从缓存读取,读到数据则直接返回;如果读不到,就读数据库,并将数据会写到缓存,并返回。
写入数据:
需要更新数据时,先更新数据库,然后把缓存里对应的数据失效掉(删掉)。

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

缓存不只是从数据库中取出来的值,有时候通过计算产生的值也会放在缓存中
还有更新缓存的代价有时候是很高的。
有时候一个数据,可能更新的比较频繁,但是不会经常访问,比如更新200多次才访问一次,如果同步进行更新缓存,开销就比较大
所以执行写操作的时候,是删除缓存,等到要访问这条数据,先去数据库读取, 然后写入缓存

数据不一致类型

  1. 数据库有数据,缓存没有数据;
  2. 数据库有数据,缓存也有数据,数据不相等;
    在执行写操作的时候,数据库修改成功了,但是缓存没有删除成功
  3. 数据库没有数据,缓存有数据。
    也是删除缓存没有成功

解决方案

  1. 对删除缓存进行重试,数据的一致性要求越高,我越是重试得快。

  2. 定期全量更新,简单地说,就是我定期把缓存全部清掉,然后再全量加载。

  3. 给所有的缓存一个失效期
    任何不一致,都可以靠失效期解决,失效期越短,数据一致性越高。但是失效期越短,查数据库就会越频繁。因此失效期应该根据业务来定。

  4. 读请求和写请求串行化,串到一个内存队列里去。
    串行化可以保证一定不会出现不一致的情况,但是它也会导致系统的吞吐量大幅度降低,用比正常情况下多几倍的机器去支撑线上的一个请求。

在并发不高的情况:

读: 读redis->没有,读mysql->把mysql数据写回redis,有的话直接从redis中取;

写: 写mysql->成功,再写redis;

可能会导致:
如果删除缓存失败了,那么会导致数据库中是新数据,缓存中是旧数据,数据就出现了不一致。

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

并发高的情况:

读: 读redis->没有,读mysql->把mysql数据写回redis,有的话直接从redis中取;

:异步话,先写入redis的缓存,就直接返回;定期或特定动作将数据保存到mysql,可以做到多次更新,一次保存;

在分布式环境下,数据的读写都是并发的,上游有多个应用,通过一个服务的多个部署(为了保证可用性,一定是部署多份的),对同一个数据进行读写,在数据库层面并发的读写并不能保证完成顺序,也就是说后发出的读请求很可能先完成(读出脏数据):

假设这会有两个请求,一个请求A做查询操作,一个请求B做更新操作,那么会有如下情形产生

(1)缓存刚好失效
(2)请求A查询数据库,得一个旧值
(3)请求B将新值写入数据库
(4)请求B删除缓存
(5)请求A将查到的旧值写入缓存

但是出现这中情况的几率比较小,数据库的读操作的速度远快于写操作的,
因此(3)耗时比步骤(2)更短,这一情形很难出现。大部分情况都是,(2)比(3)快,所以(5)比(4)先出现

解决方案

(1)设置缓存有效时间,缓存定时失效
(2)使用异步延时删除策略,保证读请求完成以后,再进行删除操作。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值