一个热点键过期后,大量请求同时查数据库,缓存本身正常也可能让下游承受突发压力。
本文使用Redis 3.0 与 Java 应用,代码为协议命令或流程示意。缓存保存的是可重建视图还是关键状态,决定了失效和故障时的处理。本文先以数据库为权威来源,不把缓存命中等同于业务事实已经确认。
1. 从现象推到机制
击穿来自同一热点缺失时的并发重建。单次重建合并、短期旧值与有限等待都可减少重复工作,但要保证锁持有者失败后其他请求能够恢复。锁超时小于重建时间时,还可能出现多个重建者。
图中的四个节点分别表示热点缺失、重建归属、请求处理、恢复清理。顺着实线检查输入如何变成输出,再沿虚线核对反馈条件。图示表达的是本文讨论的关系,不是实测曲线或现场截图。
2. 最小实现与输入条件
先统计同一键的实际回源次数,再用有界互斥和过期策略实验。锁值使用独立身份,释放需核对归属,不能直接删除任何现存锁。
1
2
3
4
GET demo:hot
SET demo:rebuild-lock unique-owner NX PX 3000
# 获得锁者重建;其他请求按约定等待或返回旧值
# 释放时原子比较 owner 后删除
在独立实例准备演示键,观察命中、过期和数据库回源次数。键包含必要的业务作用域,日志与指标避免记录完整敏感值,缓存故障路径也要能验证。
3. 用对照区分原因
下表列出需要主动改变的条件。预期行为来自机制分析,复现时还需记录运行环境和原始输出,不能将预期当作已经测得的结果。
| 改变的条件 | 需要验证的行为 |
|---|---|
| 热点键同时过期 | 回源可能形成并发尖峰 |
| 重建者执行超过锁时长 | 可能出现新的锁持有者 |
| 旧持有者无条件删除锁 | 可能删掉后来者的锁 |
对照 1:热点键同时过期
回源可能形成并发尖峰。观察时记录同键缺失与数据库请求数。把这个条件与基准情形分开,有助于判断变化来自哪一层;若得到相反结果,应先核对前提,再修改实现。
对照 2:重建者执行超过锁时长
可能出现新的锁持有者。观察时记录锁到期与任务完成时刻。把这个条件与基准情形分开,有助于判断变化来自哪一层;若得到相反结果,应先核对前提,再修改实现。
对照 3:旧持有者无条件删除锁
可能删掉后来者的锁。观察时记录释放操作是否比较归属。把这个条件与基准情形分开,有助于判断变化来自哪一层;若得到相反结果,应先核对前提,再修改实现。
4. 适用条件与取舍
锁只是控制重建并发,不保证返回数据一定新鲜。过期时间还应适当分散,避免一批键同时失效。
一次正常运行只覆盖了这组输入。继续扩展前,应把上面的异常对照保留下来,尤其关注“旧持有者无条件删除锁”这一分支。后续方案若改变了图中任意一处依赖,原来的判断就要重新验证,不能仅凭界面相似或返回值相同沿用结论。