先更新数据库再删除缓存

34 阅读2分钟

先更新数据库再删除缓存

依旧是 2 个线程并发「读写」数据:

缓存中 X 不存在(数据库 X = 1)
线程 A 读取数据库,得到旧值(X = 1)
线程 B 更新数据库(X = 2)
线程 B 删除缓存
线程 A 将旧值写入缓存(X = 1)
最终 X 的值在缓存中是 1(旧值),在数据库中是 2(新值),也发生不一致。

这种情况「理论」来说是可能发生的,但实际真的有可能发生吗?

其实概率「很低」,这是因为它必须满足 3 个条件:

缓存刚好已失效
读请求 + 写请求并发
更新数据库 + 删除缓存的时间(步骤 3-4),要比读数据库 + 写缓存时间短(步骤 2 和 5)
仔细想一下,条件 3 发生的概率其实是非常低的。

因为写数据库一般会先「加锁」,所以写数据库,通常是要比读数据库的时间更长的。

如何理解这段话?

让我来解释这个并发场景:

1. 关于时序问题:

  • 读数据库(线程A):通常很快,因为是单纯的SELECT操作

  • 写数据库(线程B):通常较慢,因为涉及以下步骤:

    • 获取写锁(行锁或表锁)
    • 执行UPDATE操作
    • 写入事务日志
    • 提交事务
    • 释放锁

**2. 为什么说发生概率低: **

假设有以下时间轴:
线程A:    读DB(0.1s)  ---------------写缓存(0.1s)
线程B:           写DB(0.3s)---删缓存(0.1s)

要发生不一致,必须满足:
  • 线程B的"写DB+删缓存"(0.4s)要比线程A的"读DB+写缓存"(0.2s)更快完成
  • 但实际上写DB操作本身就比读DB慢

3. 具体分析:

正常情况: T1: 线程A读DB开始 (0.1s) T2: 线程B写DB开始 (0.3s) T3: 线程A读DB完成 T4: 线程B写DB完成 T5: 线程B删缓存 T6: 线程A写缓存

在这种时序下,线程A写入缓存时,一般线程B还在执行写DB操作,根本来不及删除缓存。

所以虽然理论上可能发生不一致,但由于数据库写操作的特性(加锁、事务等),这种情况的实际发生概率非常低。