数据库|如何保证缓存和数据库的一致性?

数据库|如何保证缓存和数据库的一致性?

文章图片

数据库|如何保证缓存和数据库的一致性?

文章图片

数据库|如何保证缓存和数据库的一致性?

文章图片

数据库|如何保证缓存和数据库的一致性?

文章图片


很多小伙伴在面试的时候 , 应该都遇到过类似的问题 , 如何确保缓存和数据库的一致性?
如果你对这个问题有过研究 , 应该可以发现这个问题其实很好回答 , 如果第一次听到或者第一次遇到这个问题 , 估计会有点懵 , 今天我们来聊聊这个话题 。
1. 问题分析首先我们来看看为什么会有这个问题!
我们在日常开发中 , 为了提高数据响应速度 , 可能会将一些热点数据保存在缓存中 , 这样就不用每次都去数据库中查询了 , 可以有效提高服务端的响应速度 , 那么目前我们最常使用的缓存就是 Redis 了 。
用 Redis 做缓存 , 并不是一说缓存就是 Redis , 还是要结合业务的具体情况 , 我们可以根据不同业务对数据要求的实时性不同 , 将数据分为三级 , 以电商项目为例:

  • 第 1 级:订单数据和支付流水数据:这两块数据对实时性和精确性要求很高 , 所以一般是不需要添加缓存的 , 直接操作数据库即可 。
  • 第 2 级:用户相关数据:这些数据和用户相关 , 具有读多写少的特征 , 所以我们使用 redis 进行缓存 。
  • 第 3 级:支付配置信息:这些数据和用户无关 , 具有数据量小 , 频繁读 , 几乎不修改的特征 , 所以我们使用本地内存进行缓存 。
选中合适的数据存入 Redis 之后 , 接下来 , 每当要读取数据的时候 , 就先去 Redis 中看看有没有 , 如果有就直接返回;如果没有 , 则去数据库中读取 , 并且将从数据库中读取到的数据缓存到 Redis 中 , 大致上就是这样一个流程 , 读取数据的这个流程实际上是比较清晰也比较简单的 , 没啥好说的 。
然而 , 当数据存入缓存之后 , 如果需要更新的话 , 往往会来带另外的问题:
  • 当有数据需要更新的时候 , 先更新缓存还是先更新数据库?如何确保更新缓存和更新数据库这两个操作的原子性?
  • 更新缓存的时候该怎么更新?修改还是删除?
怎么办?正常来说 , 我们有四种方案:
  • 先更新缓存 , 再更新数据库 。
  • 先更新数据库 , 再更新缓存 。
  • 先淘汰缓存 , 再更新数据库 。
  • 先更新数据库 , 再淘汰缓存 。
到底使用哪种?
在回答这个问题之前 , 我们不妨先来看看三个经典的缓存模式:
  • Cache-Aside
  • Read-Through/Write through
  • Write Behind
2. Cache-AsideCache-Aside , 中文也叫旁路缓存模式 , 如果我们能够在项目中采用 Cache-Aside , 那么就能够尽可能的解决缓存与数据库数据不一致的问题 , 注意是尽可能的解决 , 并无法做到绝对解决 。
Cache-Aside 又分为读缓存和写缓存两种情况 , 我们分别来看 。
2.1 读缓存先来看一张流程图:

它的流程是这样:
  • 读取数据 。
  • 检查缓存中是否有需要的数据 , 如果命中缓存(Cache Hit) , 则直接返回数据 。
  • 如果没有命中缓存 , 即 Cache Miss , 那么就先去访问数据库 。
  • 将从数据库中读取到的数据设置到缓存中 。
  • 返回数据 。
这是 Cache-Aside 的读缓存流程 。
其实对于读缓存的流程而言 , 大家一般都没什么异议 , 有异议的主要是写流程 , 我们继续来看 。
2.2 写缓存先来看一张流程图:

这个写缓存的流程就比较简单 , 先更新数据库中的数据 , 然后删除旧的缓存即可 。
流程虽然简单 , 但是却引伸出来两个问题:
  • 为什么是删除旧缓存而不是更新旧缓存?
  • 为什么不先删除旧的缓存 , 然后再更新数据库?
我们来分别回答这两个问题 。
为什么是删除旧缓存而不是更新旧缓存?