有人在评论区问爆了:关于糖心的缓存套路,我把关键三步讲透了(别再瞎改)
导读:有人在评论区问爆了:关于糖心的缓存套路,我把关键三步讲透了(别再瞎改) 最近在一条评论下面被问到好几次:糖心这个模块的缓存到底该怎么做?有人改了TTL、有人直接把全部数据都塞Redis,结果线上乱套。把经验浓缩成三步:明确边界、做好键与一致性、保证回退与可观测。按这三步走,别再随意改配置了——改之前先想清楚后果。 先说结论(先看够用的三句话) 明确...
有人在评论区问爆了:关于糖心的缓存套路,我把关键三步讲透了(别再瞎改)

最近在一条评论下面被问到好几次:糖心这个模块的缓存到底该怎么做?有人改了TTL、有人直接把全部数据都塞Redis,结果线上乱套。把经验浓缩成三步:明确边界、做好键与一致性、保证回退与可观测。按这三步走,别再随意改配置了——改之前先想清楚后果。
先说结论(先看够用的三句话)
- 明确哪些数据可缓存、缓存多长时间,按业务价值分层缓存。
- 设计可控的缓存键与失效策略,避免全量失效与缓存污染。
- 上线要带回退、打点和限流,防止缓存穿透/击穿/雪崩。
下面把每一步拆开讲透,配上具体可落地的做法和常见坑。
第一步:明确缓存边界与分层(为什么要缓存、缓存什么) 目的:把“能缓存”和“不能缓存”分清,避免把动态敏感数据当成静态缓存。
怎么做:
- 划分数据类型
- 静态且低频变更:CDN/浏览器缓存(例如图片、静态页面片段)
- 半静态:中等TTL的缓存层(例如用户非敏感偏好、商品页的价格快照)
- 高度动态或强一致性要求的数据:不缓存或只做短TTL并实时回源
- TTL策略
- 业务决定TTL,不要凭感觉。例如商品详情:主图/描述可长缓存(1小时~24小时),库存/价格短缓存(5~60秒)。
- 使用分层TTL:edge(CDN)长TTL + origin short TTL + stale-while-revalidate策略
- 缓存的位置
- 静态资源用CDN或浏览器
- 热点数据放内存(应用进程/本地LRU)
- 大量读但需要共享的放Redis/缓存集群
- 要点示例
- Cache-Control: public, max-age=3600, stale-while-revalidate=60(适合可容忍短暂过期的视图片段)
- 对于私有用户数据,加上Vary/Cookie或放到用户会话级别缓存,不要误用public缓存
第二步:缓存键、版本与一致性(别改键名、别整个清掉) 目标:定义稳定、可控的键策略,处理更新、回滚和缓存失效的一致性问题。
怎么做:
- 设计键规则(可读且有版本)
- 格式示例:svc:entity:entityId:v1
- 把版本号包含到键里(v1、v2),需要改变结构时增加版本,而不是全局清库
- 细粒度与组合键
- 能按业务维度拆的就拆:而不是把用户A、B的结果合并成一个大key
- 对于组合查询(多维筛选),避免缓存所有组合,优先缓存基础实体,并对组合查询做短TTL或缓存计算结果
- 缓存失效策略
- 避免全表/全库清空:优先使用逐条删除或基于标签(surrogate keys)做批量清除
- 使用消息总线同步失效事件(服务A更新后发送消息,消费端删除或刷新对应键)
- 防止缓存污染与穿透
- 对空结果也缓存(短TTL,如30s),避免热点空查穿透数据库
- 对用户可控拼接缓存键中的敏感字段时要小心,避免信息泄露
- 防止缓存击穿(高并发缓存失效时打爆后端)
- 单点“重建”锁:使用分布式锁、Redis的SETNX或singleflight模式(Go)做请求合并
- 使用互斥重建或预刷新(提前随机过期,减少同一时刻大量并发回源)
第三步:上线策略、监控与回退(别瞎改TTL就推到生产) 目标:任何缓存改动都要可观测、可回退,并且有降级策略,保证系统稳定。
怎么做:
- 渐进式发布
- 先在内网/小流量灰度,观察命中率、后端QPS、延迟
- 分阶段扩大,最后全量
- 指标监控(必须有)
- Cache hit ratio(总体与热点分割)
- 后端QPS、99%/95%延迟、错误率
- 缓存命中后的响应时间 vs 回源响应时间
- 缓存容量、eviction率、内存使用
- 告警与阈值
- 命中率急剧下降、回源QPS上升、错误率增长,这些都应触发自动告警和回滚机制
- 回退与降级
- 能快速回退的改动:通过键版本切换回旧版本、或切换配置开关(feature flag)
- 降级策略:失败则走后端直接读取并对用户做有限功能提示,避免抛全站错误
- 测试场景
- 大并发下的缓存失效重建验证(压力测试)
- 回源延迟模拟(打断缓存层看看系统降级表现)
- 数据一致性测试(更新-读取顺序验)
常见误区与对策(摘抄自“不要再瞎改”的教训)
- 误区:随便把TTL从60改成3600以为能减少压力
- 后果:旧数据长期留存,更新不可控。对策:先灰度、观察业务不一致影响后再放量改动
- 误区:把所有接口都透支给Redis,忽略数据访问模式
- 后果:缓存变成单点瓶颈、热点key被碰坏。对策:分层缓存、设置本地缓存+二级缓存
- 误区:遇到缓存穿透就把空结果不缓存
- 后果:数据库持续被空请求打爆。对策:对空结果设置短TTL并加防穿透策略(布隆过滤器或白名单)
- 误区:直接清空缓存来“解决”问题
- 后果:造成瞬时后端雪崩。对策:使用逐条删除或版本切换/标签失效,配合限流
落地小示例(实操友好)
- HTTP层缓存头例子(适合视图片段)
- Cache-Control: public, max-age=3600, stale-while-revalidate=60
- Redis短TTL写入示例(伪代码)
- SET cache:product:123 "{json}" EX 60
- 防击穿简单模式(伪代码)
- if cache miss: if acquirelock("lock:product:123", timeout=3s): data = db.get(123) redis.set(key, data, ex=60) releaselock(…) else: sleep(50ms) retry reading cache
发布前的快速检查表(复制粘贴用)
- 数据分类、TTL、缓存位置是否明确?
- 键规则是否带版本?是否可能被误用为全局清理?
- 是否对空结果、异常回源、热点key做了防护?
- 是否有逐步上线、打点和告警?回退路径是否就绪?
- 灰度期间命中率与回源QPS的基线是否记录?
结语 糖心的缓存不是随手一改的配置项,而是影响全链路稳定性的设计点。按“边界—键与一致性—上线与监控”三步走,任何改动先做小流量灰度、带监控再放量,就不会因为一处TTL改动把线上拉垮。再强调一遍:别直接全库清空、别随意把敏感或强一致性数据长时间缓存、改动前先想清楚回退方案。
蘑菇视频版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!
