sbCSDN,教的东西是错的,我对着绕了好一会儿

旁路缓存

流程:

  • 写策略:先更新数据库里的数据,再删除缓存
  • 读策略:如果命中缓存,直接返回数据,没有命中,则从数据库中读取,然后将数据写入缓存,再返回数据
    缺点:存在极低概率的并发问题(读操作比写操作慢得多时,可能导致旧数据覆盖),且严重依赖“删除缓存”这一步的成功

延迟双删

流程:删除缓存->更新数据库->休眠一会儿->再次删除数据

第二次删是解决在数据库更新中,其他线程读取到旧数据并写入缓存的问题,中间需要延迟的原因是等待读取了旧数据的线程把缓存写入

既然数据库在更新过程中,其他线程读取的是旧数据,最后都需要第二次删除,那我为什么不留着缓存,这样还能减少数据库的压力

第一次删除实际是把读旧缓存变成了 读旧数据库 + 回写旧缓存,需要第一次删除其实是为了容错,防止缓存删除失败,造成的永久脏数据,有了第一次删除,即使后面都挂了,缓存也是空的,直接查数据库也可以获得正确的数据(或者是旧库的数据)

延迟双删不是有脏数据回流吗,如果延迟双删的第二次删除失败的话,他不是也会有永久脏数据吗?

从这个角度看,延迟双删在“防删除失败”这一点上,和“旁路缓存”的策略相比,没有任何优势,甚至更脆弱(因为它依赖两次删除都成功)。

那为什么还要用延迟双删?它的底线在哪里?
既然都有风险,延迟双删存在的意义是什么?

我们要区分两种“错误”:

  • A 类错误:逻辑时序错误(并发导致)

特征:网络正常,Redis 正常,代码执行成功,但因为线程执行顺序不对,导致脏数据。

Cache Aside(先改库后删):无法解决。

延迟双删:可以解决。

  • B 类错误:IO 故障错误(网络/宕机导致)

特征:执行失败。

Cache Aside:最后一步删除失败,凉凉。

延迟双删:最后一步删除失败,凉凉。

那么如何解决第二次删除失败呢,这就需要消息队列的重试机制了