数据库

Redis 缓存模式与常见陷阱

2026-08-07 #架构#Redis#缓存

选择旁路缓存、写穿或延迟双删时,明确一致性边界。

核心思路

缓存键包含业务版本,设置带随机抖动的过期时间防止雪崩。热点空值可短暂缓存,极热键考虑本地缓存和请求合并。

落地步骤

不要把 Redis 当成无容量上限的数据库。监控内存、淘汰、命中率和大键,设计降级路径以应对缓存不可用。

验证清单

实施后不要只确认“能运行”,还应检查错误率、延迟、资源占用和异常路径。把验证命令、预期结果和回滚方式写进变更记录,后续维护会轻松很多。

小结

好的工程实践不是堆叠工具,而是用清晰边界、可观察结果和可逆变更降低长期成本。先解决最重要的问题,再逐步自动化。

可执行实践

缓存键包含业务与 schema 版本,TTL 加随机抖动;空结果短暂缓存防穿透,热点回源使用请求合并。写路径明确允许的短暂不一致窗口。

验证与退出条件

监控命中率、内存、evicted_keys、过期和大键。演练 Redis 不可用时应用能限流或回源,禁止缓存故障直接拖垮数据库。

实践场景

以“为热点商品详情增加缓存”为例。实施前先保存当前版本、配置和关键指标基线,写明成功条件与停止条件;涉及生产数据或外部写入时,先在隔离环境验证。

分步实施

定义 key 版本、TTL 抖动、空值缓存和失效责任;读路径允许回源,写路径选择旁路或事件失效并记录延迟。每完成一步都记录可观察结果,不把命令返回零等同于业务正确。

故障演练

模拟击穿、穿透、雪崩、Redis 超时和旧事件乱序,确认数据库不会被瞬时打满。演练应使用可清理的测试数据,并验证告警、日志和恢复路径,而不只是确认系统最终又能访问。

观测与回滚

观察命中率、回源 QPS 和陈旧窗口;异常时关闭缓存读但保留数据库保护与限流。变更单中应写清负责人、观察窗口、阈值和回滚触发器,避免故障发生后再临时争论。

完成后把使用的版本、关键命令、观测窗口和结果写入变更记录。没有验证证据的“已完成”不能作为上线结论。

第二轮实践复核

可复用交付物: Key 规范、TTL 策略、回源保护和失效事件。复用时先固定软件版本、依赖锁文件、测试数据规模和运行环境;文章中的命令包含占位符,必须替换成自己的非生产目标后再执行。

最低验证: redis-cli --scan --pattern "cache:*" | head; redis-cli TTL cache:sample

执行记录要保存命令、退出码、关键输出、观察窗口和回滚结果。若验证会产生写入,先使用隔离环境或 dry-run;任何失败都不能通过扩大权限、关闭校验或跳过测试来“解决”。

完整实战:商品详情 Cache-Aside

读路径先查缓存,未命中再读数据库并写入;写路径先提交数据库,再删除缓存。删除而不是直接更新能减少多写入者的竞态,但仍存在读请求在删除后写回旧值的窗口,因此缓存只承载可容忍短暂陈旧的数据。

1
2
3
key = product:{tenant}:{product_id}:v3
value = {schema_version, product, source_updated_at}
ttl = base_ttl + random_jitter

键包含租户和 schema 版本,TTL 加抖动避免同批键同时过期。值中保留来源版本,便于诊断陈旧数据。不能把权限判断结果按错误维度共享,也不能让缓存命中绕过当前用户鉴权。

防击穿、穿透与雪崩

风险 表现 处理
热点击穿 一个热键过期,大量请求回源 singleflight/短锁 + 过期数据兜底
缓存穿透 不存在 ID 持续回源 输入校验、短 TTL 空值、限流
集中过期 数据库瞬间升载 TTL 抖动、分批预热、容量保护
Redis 故障 所有请求直接冲击数据库 回源并发上限、熔断、负载削减

分布式锁必须有过期和持有者 token,只允许持有者释放;即使有锁,数据库仍需承受锁服务失效。对强一致余额、权限撤销等数据,不要用普通 cache-aside 假装强一致。

验证缓存是否真的有收益

同时看命中率、按 keyspace 的 P95、回源 QPS、过期/淘汰、内存碎片、热键分布和数据库负载。高命中率也可能掩盖序列化很慢或数据长期陈旧。测试冷启动、热点键、Redis 超时、批量过期和数据库降速,确认回源有界且不会形成重试风暴。

上线先给少量租户启用,保留绕过缓存的开关。回滚时关闭读缓存并停止填充即可,不要为了“清干净”对共享 Redis 执行宽泛删除;旧版本键依靠命名空间和 TTL 自然退出。

一手资料

资料与版本边界复核日期:2026-08-22。工具行为可能演进,实施前以锁定版本对应的官方文档为准。

评论
分享

评论