Redis和MySQL如何保证数据一致性?


一、背景

一般情况下,Redis是用来实现应用和数据库之间读操作的缓存层,主要目的是减少数据库IO,还可以用来提升数据的IO性能

二、设计思想

当应用需要读取某个数据的时候,会有几个步骤:

  • 尝试去Redis查询,如果命中就直接返回
  • 如果没有命中,则从数据库查询
  • 同步数据库数据到Redis

三、分析可能出现问题的场景

当数据发生变化的时候,需要同时更新Redis和MySQL,原先方案有:
先更新数据库,再更新缓存

  • 问题1:如果更新数据库成功,还没来得及更新缓存,这时候有另外一个客户端去访问了缓存,访问到了是旧的数据,这时候就会出现数据不一致
  • 问题2:如果缓存更新失败,也会导致数据不一致

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

  • 预期:应用下次请求数据,发现缓存没数据,则从数据库同步数据到缓存里
  • 分析:极端情况下,由于删除缓存和更新数据库的这两个操作不是原子的,所以还是会出现数据不一致
  • 问题:如果删除了缓存,还没来得及更新数据库,这时候有另外一个客户端去访问了缓存,发现没数据,所以去数据库同步了旧的数据,这时候数据库才更新了新的数据,就会出现数据不一致

四、解决方案:延迟双删

解决数据一致性比较简单的方案有延迟双删,步骤为:

  1. 删除缓存
  2. 可能有其他客户端读操作(查到缓存为空,去数据库差旧的数据)
  3. 更新数据库
  4. 延迟删除缓存(因为可能第二部的读操作还没执行完成,如果立马删除的画,读操作还没来得及写缓存,那么又会把缓存更新成旧的数据)

但是延迟双删也有缺点,就是性能低,不适合高并发场景
所以在极端情况下仍然需要保证Redis和MySQL的数据一致性,就只能采用最终一致性方案

五、解决方案:最终一致性

介绍:由于延迟双删性能低,在高并发场景下并不合适,所以出现了最终一致性的方案,它保证了最终一致性,但是可能会出现短期不一致性(如果场景不能接受短期不一致性,可以采用读写锁等的方式实现强一致性,不过性能上会有一定影响)

1、采用MQ的重试机制

步骤

  1. 更新数据库
  2. 删除缓存(需要保证删除成功!!!
  3. 如果删除失败,则发送到MQ,使用其重试+手动确认的机制保证删除成功

缺点:高耦合,每个步骤都要发MQ,比较麻烦

2、用canal组件

原理:通过监听MySQL中binlog日志,把更新后的数据同步到Redis里面
优点:低耦合,省去了每步都需要发MQ的流程


文章作者: GaryLee
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 GaryLee !
  目录