Redis缓存一致性问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liuzhixiong_521/article/details/84588549

前言

从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案。这种方案下,我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努力即可。也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读取新值然后更新缓存。
因此,接下来讨论的思路不依赖于给缓存设置过期时间这个方案。

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

一、线程安全问题

假设同时有请求A和请求B进行更新操作,那么会出现如下情景

  1. 线程A更新了数据库
  2. 线程B更新了数据库
  3. 线程B更新了缓存
  4. 线程A更新了缓存

正确结果应为缓存A数据,但是因为网络原因或者其他原因,导致缓存了B数据,出现脏数据问题。

二、使用场景问题

  • 若写请求较多,读请求较少,导致缓存数据被频繁更新,浪费性能。
  • 若缓存数据由数据库数据计算得来,则每次写库都要耗费CPU性能做计算更新缓存,浪费性能。

综上,先更新数据库,再更新缓存不适用于作为redis缓存一致性解决方案。

先删缓存,再更新数据库

一、问题

假设同时有请求A进行更新操作和请求B进行读取操作,那么会出现如下情景

  1. 请求A进行写操作,删除缓存
  2. 请求B查询发现缓存不存在
  3. 请求B去数据库查询得到旧值
  4. 请求B将旧值写入缓存
  5. 请求A将新值写入数据库

上述情景则会导致缓存不一致问题出现。

二、延时双删策略

流程为

  1. 先删除缓存
  2. 再写数据库
  3. 休眠一段时间
  4. 再次删除缓存

睡眠时间如何确定?
由于睡眠的原因是为了保证,后续能够删除读库操作误更新的缓存数据,则第二次删除操作应在读库以及更新操作之后,所以睡眠时间应为读库操作时间加上几百毫秒。
如果数据库采用了读写分离架构怎么办?
睡眠时间为主从同步延迟时间加上读库耗时加上几百毫秒。
第二次删除失败解决方案

  1. 若删除失败,将需要删除的key发送至消息队列,另写代码执行删除操作,直到删除成功。
  2. 另起程序,订阅数据库的binlog,对删除失败的key进行删除操作。

先更新数据库,再删缓存

问题

假设同时有请求A进行读取操作,请求B做更新操作,那么会出现如下情景

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

但是,操作3耗时远大于操作2耗时,故这种情况出现的可能性几乎为0,可以忽略不计。
综上所述,先更新数据库,再删缓存为最优提供缓存一致性解决方案(当然,这里讲的方案不包含各种优化)。

猜你喜欢

转载自blog.csdn.net/liuzhixiong_521/article/details/84588549