PHP缓存更新策略怎么定

wen PHP项目 2

** PHP缓存更新策略怎么定?从失效、预热到一致性,告别脏数据与性能瓶颈

PHP缓存更新策略怎么定


📚 目录导读

  1. 为什么“更新缓存”比“写缓存”更难?——雪崩、穿透与一致性的根源
  2. 三大主流更新策略详解:Cache Aside(旁路)、Read Through / Write Through、Write Behind(异步回写)
  3. 实战进阶:如何选择与组合策略?——针对高并发、强一致、低延迟场景
  4. 终极保底方案:延迟双删 + 版本号/订阅通知——彻底解决缓存与数据库不一致
  5. Q&A 高频问答——面试官与架构师最关心的5个问题
  6. 制定策略的决策树与避坑清单

为什么“更新缓存”比“写缓存”更难?

在PHP开发中,Redis或Memcached通常作为数据库之上的高速缓冲层,很多初级开发者习惯先更新数据库,再删除缓存;或者先更新缓存,再写库,但这两种直觉操作,在高并发下都会产生数据不一致(脏读)或缓存雪崩问题。

核心矛盾在于:数据库事务的原子性缓存操作的独立性无法天然保持同步,用户A更新了昵称,数据库写入成功,但删除缓存时网络抖动失败,那么后续请求都会读到旧数据,直到缓存过期(可能长达数小时),这也是我们需要一套明确“更新策略”的根本原因。

三大主流更新策略详解

Cache Aside(旁路缓存)——最常用,适合读多写少

  • 读操作:先读缓存,命中则返回;未命中则读数据库,回填缓存。
  • 写操作:先更新数据库,成功后再删除缓存(而非更新缓存)。
  • 为什么删除而不是更新? 因为“更新缓存”在并发写时容易产生旧值覆盖新值的问题,而删除后,下一次读会自然回填最新数据。

Read Through / Write Through(穿透式)

  • 在缓存层(如代理层或PHP封装类)内部完成数据库读写,缓存是唯一数据源,对业务透明。
  • 优点:缓存与数据库强一致(同步写)。缺点:性能损耗大,并发吞吐量受限。

Write Behind(异步回写)

  • 先写缓存(标记为脏数据),立即返回;后台异步批量写入数据库。
  • 优点:写入性能极高,适合日志、计数等弱一致场景。缺点:宕机可能丢数据,且瞬时缓存与库不一致窗口大。

实战进阶:如何选择与组合?

  • 场景A:电商库存(强一致性要求) → 采用 Cache Aside + 消息队列(MQ)可靠删除,更新库后,发送MQ消息,消费者专门负责删缓存,失败重试。
  • 场景B:用户Feed流(高并发、可容忍秒级延迟) → 采用 Write Behind + Redis持久化,先写缓存,后台批量入库,并设置缓存过期时间兜底。
  • 场景C:PHP后台管理系统(低频写) → 直接 Cache Aside,无需过度设计,但务必开启缓存版本号。

关键结论:没有银弹,必须基于数据一致性容忍度QPS做取舍。


终极保底方案:延迟双删 + 版本号

为了解决Cache Aside中“删除缓存失败”或“并发读写竞态”问题,业界常用 延迟双删

  • 步骤:更新数据库 → 删除缓存 → 休眠几百毫秒(如500ms)→ 再次删除缓存
  • 原理:第二次删除,是为了清掉在第一次删除后、休眠期间可能被其他线程回填的旧缓存数据(因为此时数据库已提交新值)。

更稳健的做法是给缓存增加逻辑版本号(如更新时间戳):每次写库后,将缓存key的版本号+1,读时,若缓存版本号落后于数据库版本,则主动失效回源,PHP中可利用Redis的WATCH实现乐观锁版本判断。


Q&A 高频问答

Q1:为什么更新缓存时,不直接写缓存而是删缓存?

A:并发写时,若两个线程分别写“新值A”和“新值B”,后写的B可能先完成缓存写入,先写的A后写入,导致旧值覆盖新值,删除则无此问题,因为读时才会回填最新库值。

Q2:如果删缓存失败怎么办?

A:不能吞异常,必须重试机制:利用MQ延迟队列或者记录日志后定时补偿,绝不能代码里try...catch后忽略。

Q3:延迟双删的延迟时间怎么定?

A:一般设置大于一次业务写操作平均耗时 + 网络RTT,经验值200~500ms,若数据库主从复制有延迟,需延长至主从同步完成时间。

Q4:使用Write Behind时,如何防止缓存雪崩?

A:给缓存设置随机过期时间(如基础TTL + 随机0~300s),避免同一时刻全部失效,回写数据库需有重试和幂等机制。

Q5:是否可以用Redis的EXPIRE作为最终兜底?

A:必须可以!即使策略再完美,也要设置缓存TTL(如30分钟),这是数据最终一致的最后保险丝


制定策略的决策树与避坑清单

决策树速查表

  • 读多写少、容忍脏读 → Cache Aside + 双删
  • 强一致性、写并发大 → Write Through(牺牲部分性能)。
  • 高吞吐、弱一致性 → Write Behind + 随机TTL
  • 无法依赖删除操作 → 版本号对比 + 主动过期

避坑清单

  1. 禁止“先删缓存,再更新数据库”(易导致并发下缓存击穿)。
  2. 禁止“无TTL的永久缓存”。
  3. 禁止在PHP进程内用sleep()代替延迟双删(会阻塞worker),应使用Redis的DELAYED队列或独立服务。
  4. 务必监控缓存命中率与删除失败数,及时报警。

缓存的核心不是“加速”,而是“在正确的时间保存正确的数据”,策略定了,系统才能做到既快又稳。


(全文完)

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