PHP先删缓存还是后删

wen PHP项目 2

本文目录导读:

PHP先删缓存还是后删

  1. 核心结论:先更新 DB,后删缓存(Cache-Aside 模式)
  2. 为什么不建议“先删缓存,后更新 DB”?
  3. “先更新 DB,后删缓存”的并发风险与补救
  4. 最终方案建议(结合 PHP 实际场景)
  5. 特殊情况:删除缓存失败怎么办?
  6. 总结(决策表)

在 PHP 开发中,先删缓存还是后删缓存”的问题,90% 的情况下应该选择“先更新数据库,后删缓存”

这涉及经典的缓存一致性策略,以下是详细的分析和最佳实践:

核心结论:先更新 DB,后删缓存(Cache-Aside 模式)

这是业界最推荐、最稳妥的方式。

  • 步骤
    1. 更新数据库(Update DB)。
    2. 删除缓存(Delete Cache)。
  • 原理:如果数据库更新成功但删缓存失败,下次读取会从数据库加载最新数据并回填缓存,缓存会自动恢复一致,这种方式容错率最高。

为什么不建议“先删缓存,后更新 DB”?

这种顺序会导致严重的并发脏数据问题,即 Cache Aside 的经典坑

  • 场景:线程 A(写)先删除了缓存,线程 B(读)此时来查缓存,发现缓存没数据,于是去数据库查询。
  • 时序问题
    1. 线程 A 删除缓存。
    2. 线程 B 查询 DB,拿到旧数据(因为线程 A 还没更新完 DB)。
    3. 线程 B 把旧数据写回缓存。
    4. 线程 A 此时才更新 DB 为新数据
  • 结果:数据库是新数据,缓存是旧数据,缓存被污染,且可能持续很久。

“先更新 DB,后删缓存”的并发风险与补救

这个方案并非绝对完美,也存在极端情况下的竞态(Race Condition):

  • 场景
    1. 线程 A(读)缓存 Miss,查询 DB 得到旧数据。
    2. 线程 B(写)更新 DB 为新数据,并删除缓存。
    3. 线程 A 将之前查到的旧数据写回缓存。
  • 风险:缓存再次被旧数据污染。
  • 评判:这种情况发生的概率极低,因为它要求“读”线程的查询耗时比“写”线程的更新+删除耗时还长,且步骤 2 必须在步骤 1 和步骤 3 之间穿插执行。

为了规避这个极小概率问题,通常采用“延迟双删”策略:

  • 步骤
    1. 更新数据库。
    2. 删除缓存。
    3. 休眠极短时间(如 500ms,视业务而定)。
    4. 再次删除缓存
  • 作用:延迟操作可以补掉步骤 3 中“线程 A”写回的旧缓存。

最终方案建议(结合 PHP 实际场景)

推荐顺序:先写 DB,再删缓存,必要时加延迟双删。

  • 注意:如果在同一个 PHP 请求中,你刚刚更新完 DB,紧接着又要查询这条数据,此时不建议直接查缓存(因为缓存已被删),建议直接查 DB 或从返回结果中获取,避免空缓存穿透。

特殊情况:删除缓存失败怎么办?

如果你的代码执行了 delete 命令,但 Redis 返回失败(如网络抖动),此时缓存是脏的。

  • 应对方案
    • 重试机制:将删除失败的 Key 发送到消息队列(MQ),由消费者异步重试。
    • 设置过期时间:即使删缓存失败,Redis 的 EXPIRE(过期时间)是兜底方案,只要过期时间合理,最终会达到最终一致性。

决策表)

策略 同步性 并发安全性 推荐指数
先更新 DB,后删 Cache 最终一致 (推荐) ⭐⭐⭐⭐⭐
先删 Cache,后更新 DB 最终一致 低(脏数据概率大)
先更新 DB,后删 Cache + 延迟双删 最终一致 极高(极致优化) ⭐⭐⭐⭐⭐(高并发核心数据)

给 PHP 开发者的最终建议:写业务逻辑时,不要写“先删缓存再更新数据库”的代码,请无条件使用“先更新数据库,然后再删除缓存的 Key”,如果项目对一致性要求苛刻,就在删除后加一个 usleep() 或异步任务做二次删除。

抱歉,评论功能暂时关闭!