前言

在项目中引入缓存系统,我们需要考虑在对旧数据进行更新操作时,我们是先淘汰缓存,再更新数据库;还是先更新数据库,再淘汰缓存。或者是更新数据库,再更新缓存呢?下面是对这三种方案的优缺点的一些总结:

缓存更新策略

  1. 先更新数据库,再更新缓存
  2. 先更新数据库,再删除缓存
  3. 先删除缓存,再更新数据库

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

这套方案适用场景比较少,主要从下面几个原因分析:

资源浪费

在一些大型的信息网站中(博客、贴吧),我们引入缓存主要是对热数据(请求频繁的)进行缓存,而这时候,如果很多用户对于冷数据(长时间没人访问,或者访问量很少)进行更新,然后再去更新缓存,这就造成了缓存资源的大量浪费(因为访问量少,导致这些缓存命中低,浪费缓存资源)。

脏数据

这是由于出现了并发操作的原因导致的,如:同时有两个请求 A 和 B 对数据进行了更新操作,由于网络原因,可能存在以下情况:

  1. 请求 A 更新了数据库
  2. 请求 B 更新了数据库
  3. 请求 B 更新了缓存
  4. 请求 A 更新了缓存

这就出现了 A 数据覆盖了 B 数据的情况,此时就产生了脏数据,如果没有缓存定时过期机制,此时的脏数据需要等待下一次的更新,才会对缓存进行更新,虽然用户看到数据出现问题,会再重新更新一次,但这已经有多了一次不必要的请求了,写请求量大的时候,容易造成众多不必要的更新请求。

请求时间

如果缓存不是一种简单的数据缓存,而是需要经过较为复杂的运算,才能得出缓存值,这时候,请求将会在计算缓存值上,耗费一部分时间,而这就导致了请求的响应时间变长,增加系统的负担,降低了系统的处理能力。

频繁写入

在写请求很多,而读请求很少的场景下,缓存没起到多大的作用,就给频繁更新了,造成了资源浪费,如:

  1. 对数据 A 进行了一次修改,生成了缓存 A
  2. 此时没有读取数据 A 的请求
  3. 对数据 A 进行了一次修改,更新了缓存 A
  4. 此时没有读取数据 A 的请求
  5. 对数据 A 进行了一次修改
  6. 此时有了读取数据 A 的请求

这时就会造成缓存的不必要更新操作(没有人读取缓存),用户量大的时候,会造成大量的不必要操作,造成系统资源的浪费

适用场景

一般适用于下列几种场景:

  1. 读请求占据网站的总流量的 99%
  2. 网站数据量不大(几十万的文章数据)
  3. 很少会去更新数据(一般文章写好后,不会去更新)

案例

  1. 个人博客
  2. 手册网站(w3cschool、菜鸟教程等)

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

这套方案比较多的平台适用,如 FaceBook。但也存在一些问题:

脏数据

造成脏数据的原因主要由并发引起,如:

  1. 用户 A 请求数据 A
  2. 数据 A 缓存失效
  3. 用户 A 从数据库中得到旧数据数据 A
  4. 用户 B 更新了数据 A(新数据)
  5. 用户 B 删除了缓存
  6. 用户 A 将查到旧数据写入了缓存

此时就产生了脏数据,虽然这种概率非常小,但对于更新不频繁的网站来说,此时的脏数据就是个很严重的错误。

缓存删除失效

  1. 用户 A 更新了数据 A
  2. 用户 A 删除数据 A 的缓存失败
  3. 用户 B 读到数据 A 缓存的旧数据

此时就产生了数据不一致的问题。

解决方案:

设置缓存的有效时间(最简单的解决方案)

优点:

  • 简单
  • 易操作

缺点:

  • 会存在短时间内的旧数据
  • 如果数据量太多,缓存有效时间短,容易发生一段时间内缓存大量失效,此时的数据库压力突然剧增,引发缓存雪崩现象(缓存有效时间为随机值减少发生缓存雪崩的可能性)

消息队列(增加复杂性,需要引入消息队列系统)

步骤:

  1. 更新数据库
  2. 删除缓存失败
  3. 将需要删除的 Key 发送到消息队列
  4. 隔断时间从消息队列中拉取要删除的 key
  5. 继续删除,直至成功为止

优点:

  • 不会引发缓存雪崩
  • 保证了缓存的删除
  • 不会增加更新的处理时间

缺点:

  • 引入了消息队列系统,增加了系统的复杂性

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

这种方案是比较多人使用的,但也会出现脏数据问题

原因:

  1. 用户 A 删除缓存失败
  2. 用户 A 成功更新了数据

或者

  1. 用户 A 删除了缓存
  2. 用户 B 读取缓存,缓存不存在
  3. 用户 B 从数据库拿到旧数据
  4. 用户 B 更新了缓存
  5. 用户 A 更新了数据

以上两种情况都能造成脏数据的产生

解决方案:

设置缓存的有效时间(最简单的解决方案)

优点:

  • 简单
  • 易操作

缺点:

  • 会存在短时间内的旧数据
  • 如果数据量太多,缓存有效时间短,容易发生一段时间内缓存大量失效,此时的数据库压力突然剧增,引发缓存雪崩现象(缓存有效时间为随机值减少发生缓存雪崩的可能性)

消息队列(增加复杂性,需要引入消息队列系统)

步骤:

  1. 更新数据库
  2. 删除缓存失败
  3. 将需要删除的 Key 发送到消息队列
  4. 隔断时间从消息队列中拉取要删除的 key
  5. 继续删除,直至成功为止

优点:

  • 不会引发缓存雪崩
  • 保证了缓存的删除
  • 不会增加更新的处理时间

缺点:

  • 引入了消息队列系统,增加了系统的复杂性

总结:

  1. 以上的几种方案需根据自己的业务来选择
  2. 存在即合理,各有优缺点
  3. 使用设置缓存失效时间时,需注意缓存雪崩问题
  4. 可以使用消息队列来避免脏数据的出现