数据库和缓存数据一致性问题

业务使用Redis做缓存,当有数据更新时,如何保证缓存及时更新

读数据流程

请求到来,业务代码会先查Redis,查不到再去查DB,并将结果写入Redis

写数据方案

1. 先删除缓存,再更新DB

可行性

先删除缓存,再更新DB,下次读请求到来会从数据库查到新的数据更新到缓存中。如果先更新缓存,在更新DB,更新DB失败会导致数据不一致。

问题

容灾不足

如果删除缓存失败的情况,如果业务继续进行,更新DB,那么在缓存过期之前仍然查到的是旧数据。如果业务返回失败,则对Redis变成了强依赖。

并发不安全

考虑如下场景:

  1. A请求删除缓存,A请求更新DB
  2. B请求查询缓存,不存在
  3. B请求查询DB,查到旧数据(更新未完成),写入缓存
  4. A请求更新DB完成

这就导致缓存中仍存的旧数据,数据不一致。

2. 先更新DB,再删除缓存

这种策略解决了方法1中的并发问题,但是还是有极小可能存在并发问题,考虑如下情况:

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

这种情况确实会产生数据不一致,但是考虑到DB的读操作总是比写操作快的多,这种场景基本不可能出现。

如何杜绝并发问题

延迟异步删,保证读操作完成后再删除缓存。

如何容灾

上述方案中如果删除缓存失败了怎么办?

引入消息队列
  1. 更新DB
  2. 删除缓存,如果失败将要删除的key发送至消息队列
  3. 消费消息,获得需要删除的key,删除key缓存直到成功
订阅binlog

上述方法对业务代码的侵入性比较大,为此可以启动一个程序订阅MySQL的binlog用来发现数据更新,流程如下:

  1. 业务代码更新数据库,MySQL将更新操作写入binlog
  2. 订阅程序提取中更新的数据以及key,尝试删除key的缓存
  3. 如果删除缓存失败,将key发送至消息队列
  4. 消费者程序从消息队列中获取待删除的key,重试删除直到成功。

参考

【1】Redis与Mysql双写一致性方案解析

你可能感兴趣的:(数据库和缓存数据一致性问题)