某互联网电商平台采用 MySQL + Redis的缓存架构。核心接口为“查询商品详情”,日常峰值 QPS 达到几万。系统目前使用 Cache-Aside 模式:先查 Redis 缓存,命中则直接返回;未命中时访问数据库,并把结果写回 Redis,设置过期时间(TTL)。
近期运维发现:某些热点商品在缓存失效瞬间,会出现大量请求同时击穿缓存、直接访问数据库,导致数据库连接数飙升、接口响应超时,影响系统整体 可用性。架构组组织评审,提出了两种优化思路:
方案一(王工):基于互斥锁的物理过期方案。仍然依赖 Redis TTL,缓存正常按过期时间删除。当缓存未命中时,只有拿到互斥锁的线程才允许去数据库重建缓存,其它线程等待或重试。
方案二(李工):逻辑过期方案(不依赖互斥锁做强同步,可返回过期数据):Redis 中存储“业务数据 + 逻辑过期时间字段”,可以不设置或设置很长 TTL。访问时若发现逻辑过期,可以先返回旧数据,同时由获取到非互斥锁的线程或后台异步线程去数据库重建缓存。
请根据王工“基于互斥锁的缓存查询过程”(物理过期方案)的主要处理步骤,把下方的序列图补充完整。

(1)获取互斥锁成功(2)查询数据库重建缓存数据(3)写入缓存(4)释放锁(5)获取锁失败

本题考察的是 基于互斥锁防止缓存击穿 的典型流程,以及如何用序列图刻画两个并发线程在同一热点 Key 上的协作关系。关键点是:只有一个线程真正访问数据库并重建缓存,其它线程都通过等待与重试的方式复用已经重建好的缓存,从而保护数据库不被突发流量冲击。
在补图时,应先理清时间线:线程 1 先到达,线程 2 后到达。两者在步骤 1 和 2 都做了“查询缓存”,因为缓存物理过期,结果都是未命中。接下来线程 1 在步骤 3 成功获得互斥锁(一般用 Redis SETNX 实现),说明它成为“重建者”;线程 2 在步骤 4 获取锁失败,因此不能再去数据库,只能进入步骤 6“休眠一会儿再重试”。这体现了互斥锁的核心目的:串行化对数据库的访问。随后线程 1 在持锁状态下执行步骤 5“查询数据库重建缓存数据”,拿到结果后在步骤 7 将数据写入缓存,最后在步骤 8 释放锁。到这里,缓存中已经重新有了最新的数据。
而线程 2 在休眠一段时间后被唤醒,根据王工的流程,不是直接再去加锁,而是先重新查询缓存。若缓存已经被线程 1 重建,则这次查询会命中,这就是序列图中步骤 9“缓存命中”。线程 2 直接返回数据,不需要访问数据库,也不再参与锁竞争。这部分在画图时容易漏掉:6 后面必须有“再次查询缓存”的消息线,才能解释为何 9 是“命中”,也体现了“等待别人重建好缓存后自己复用”的设计思想。
通过这个序列图可以看出:互斥锁方案的优点是数据库只被一个线程访问,避免热点 key 击穿;缺点是其他线程在等待期间被阻塞或轮询,对响应时间有一定影响。因此在题目的大案例中,还需要和逻辑过期方案结合“高可用”要求进行权衡。