Redis缓存一致性问题

目录

1、背景

2、缓存读写模式

2.1、Cache-Aside Pattern(旁路缓存模式)

2.2、Read-Through/Write-Through(读写穿透模式)

2.3、Write Behind Pattern(异步缓存写入)

3、数据不一致的几种场景

3.1、先删缓存,再更新数据库

3.2、先更新数据库,再删缓存

4、解决方案

4.1、延时双删

4.2、消息队列

4.3、进阶版消息队列(基于订阅binlog的同步机制)

4.4、数据对比

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


1、背景

        高并发场景下,如果让所有的请求都直接访问数据库,大概率数据库是扛不住这么大的并发压力的。所以,通常情况下,我们会使用Redis做一个缓冲操作,请求来时,先去查询Redis缓存,如果能在缓存中查到数据,则直接返回;如果查不到,再去查询数据库。

        读场景下,Redis和数据库在数据一致性层面没什么问题,但是,在涉及到数据更新操作时:数据库和缓存更新,就会存在数据一致性问题。

2、缓存读写模式

2.1、Cache-Aside Pattern(旁路缓存模式)

        适用于读多写少的场景。使用该模式的系统对缓存失效具有一定的弹性,即使缓存集群宕机,系统仍然可以通过访问数据库获取到数据,只不过这种情况下可能会降低响应时间,严重的话会搞崩数据库。

读流程:

  • 读的时候,先读缓存,缓存命中的话,直接返回数据
  • 缓存没有命中的话,就去读数据库,从数据库取出数据,放入缓存后,同时返回响应。

写流程:

  • 更新的时候,先更新数据库,然后再删除缓存

2.2、Read-Through/Write-Through(读写穿透模式)

        Cache-Aside模式下,业务系统需要同时维护缓存和数据库,以此来保证缓存和数据库的数据一致性。

        Read/Write Through模式下,提供一个缓存服务,由缓存服务维护缓存与数据库的数据一致性,业务系统只需要与缓存服务进行读写即可,不必再关心缓存与数据库的一致性问题。

读流程:

  • 业务系统发读请求给缓存服务
  • 缓存服务:从缓存读取数据,读到直接返回
  • 缓存服务:如果读取不到的话,从数据库加载,写入缓存后,再返回响应。

写流程:

  • 业务系统发写请求给缓存服务
  • 缓存服务:更新数据库
  • 缓存服务:更新缓存

2.3、Write Behind Pattern(异步缓存写入)

        类比Read-Through/Write-Through模式,都是由一个缓存服务负责缓存和数据库的读写。不同点在于,Write Behind Pattern模式执行写操作时,只更新缓存,不直接更新数据库,通过异步批量的方式写入数据库。

        数据存储的写性能很高,但数据的一致性变差,极端场景下可能会丢失数据。

        适用于变更频率很高,但对一致性要求不太高的业务场景。

3、数据不一致的几种场景

        由于数据库和缓存(比如Redis)是两个组件,所以不可能在一个事务中处理,那么数据的更新就会存在一个先后问题。

3.1、先删缓存,再更新数据库

原始数据:缓存中有key = value1,数据库有key = value1。

  1. 线程1删除缓存key成功
  2. 线程2读取数据,缓存key已被删除,从数据库读取key=value1,并写入缓存key=value1
  3. 线程1更新数据库key = value2

此时,缓存中key=value1,数据库中key=value2,数据不一致。

3.2、先更新数据库,再删缓存

原始数据:缓存中有key = value1,数据库有key = value1。

  1. 线程1更新数据库key = value2;
  2. 线程1删除缓存key,删除失败。

此时,缓存中key=value1,数据库中key=value2,数据不一致。

4、解决方案

4.1、延时双删

        在写库前后都进行redis.del(key)操作,并且设定合理的超时时间。

  • 删除缓存
  • 更新数据库
  • 休眠500毫秒(休眠时间由读业务逻辑耗时决定,确保读请求结束后,写请求可以删除读请求造成的缓存脏数据)
  • 再次删除缓存
  • 读请求更新缓存时,给缓存设置超时时间

4.2、消息队列

        先更新数据库,成功后往消息队列发消息,消费到消息后再删除缓存,借助消息队列的重试机制来实现,达到最终一致性的效果。

        但是,引入消息队列后,就需要考虑消息中间件的维护问题、消息丢失问题、消息延迟等问题。

4.3、进阶版消息队列(基于订阅binlog的同步机制)

        MySQL binlog增量订阅消费+消息队列+增量数据更新到redis。

  • 读Redis:热数据基本都在Redis
  • 写MySQL:增删改都是操作MySQL
  • 更新Redis数据:MySQ的数据操作binlog,来更新到Redis

        使用canal订阅mysql的binlog,一旦MySQL中产生了新的读写、更新、删除操作,通过消息队列,就可以把binlog相关的消息推送至Redis,Redis再根据binlog中的记录,对Redis进行更新。

4.4、数据对比

        通过数据对比模块,发现差异,差异报警,自动修复,定时将mysql数据同步到redis中。

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

如果是更新的话,那就是先更新数据库,再更新缓存

举个例子:如果数据库1小时内更新了1000次,那么缓存也要更新1000次,但是这个缓存可能在1小时内只被读取了1次,那么这1000次的更新有必要吗?

反过来,如果是删除的话,就算数据库更新了1000次,那么也只是做了1次缓存删除,只有当缓存真正被读取的时候才去数据库加载。

以上内容为个人学习理解,如有问题,欢迎在评论区指出。

部分内容截取自网络,如有侵权,联系作者删除。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值