# 1.情况总结

  • 两种更新情况 更新缓存的顺序是不确定的
  • 先删缓存 读到错误数据插入缓存
  • 后删缓存 删除了正确的数据
  • 延迟双删 删除缓存两次

# 2.先更新数据库,再更新缓存

并发场景下,会出现缓存和数据库不一致的情况,请求 A 更新数据库为 1,这时还未更新缓存,请求 B 更新了数据库为 2,并且更新了缓存为 2,然后请求 A 更新了缓存为 1,导致数据库为 2,缓存为 1,数据库和缓存不一致。

image-20230905002728785

# 3.先更新缓存,再更新数据库

请求 A 更新缓存为 1,这时还未更新数据库,请求 B 更新了缓存为 2,并且更新了数据库为 2,然后请求 A 更新了数据库为 1,导致缓存为 2,数据库为 1,数据库和缓存不一致。

image-20230905002740095

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

请求 A 更新的时候先删除缓存,还未更新数据库,此时,请求 B 查询缓存,未查到数据,请求 B 查询数据库,然后更新缓存为 20,然后请求 A 更新数据库为 21,导致缓存和数据库不一致。这种情况是因为更新数据和读数据导致的缓存和数据库一致性问题。前面提到的两种都是覆盖导致的问题。

image-20230905002752284

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

请求 A 读取数据时,缓存没有命中,读取数据库的值为 20。此时请求 B 是更新操作,先更新了数据库为 21,然后删除了缓存。然后请求 A 接着操作,写缓存将 20 的数据写入到缓存中,导致数据库与缓存数据不一致。

在更新数据时,不更新缓存,而是删除缓存中的数据。然后,到读取数据时,发现缓存中没了数据之后,再从数据库中读取数据,更新到缓存中。Cache Aside 策略,中文是叫旁路缓存策略。这种策略出现问题的概率最小,因为一般情况下读是比写要快很多的。

image-20230905002818496

从上面的理论上分析,先更新数据库,再删除缓存也是会出现数据不一致性的问题,但是在实际中,这个问题出现的概率并不高。

因为缓存的写入通常要远远快于数据库的写入,所以在实际中很难出现请求 B 已经更新了数据库并且删除了缓存,请求 A 才更新完缓存的情况。

所以,「先更新数据库 + 再删除缓存」的方案,是可以保证数据一致性的.

如何保证 2 个消息成功呢?

  • 「消息队列来重试缓存的删除」
  • 「订阅 MySQL binlog 再操作缓存」

# 6.延迟双删

#删除缓存
redis.delKey(X)
#更新数据库
db.update(X)
#睡眠
Thread.sleep(N)
#再删除缓存
redis.delKey(X)
1
2
3
4
5
6
7
8

加了个睡眠时间,主要是为了确保请求 A 在睡眠的时候,请求 B 能够在这这一段时间完成「从数据库读取数据,再把缺失的缓存写入缓存」的操作,然后请求 A 睡眠完,再删除缓存。所以,请求 A 的睡眠时间就需要大于请求 B 「从数据库读取数据 + 写入缓存」的时间。

但是具体睡眠多久其实是个玄学,很难评估出来,所以这个方案也只是尽可能保证一致性而已,极端情况下,依然也会出现缓存不一致的现象。因此,还是比较建议用「先更新数据库,再删除缓存」的方案。

上次更新: 11/2/2024, 2:13:44 PM