解决Redis缓存预热不充分导致击穿的自动化脚本方案
提到Redis缓存预热不充分,许多开发者首先想到的是“完全没做预热”,但现实往往更为隐蔽:即使预热脚本执行了、数据写入了,上线后短短两三分钟内,缓存仍然被击穿。根本问题在于——预热的数据并未覆盖真正的访问热点。可能的原因是热点列表取自上周的离线统计、脚本未跑完就仓促上线,或者热点已更新但预热未同步刷新。总之,问题集中爆发在上线后的前几分钟,一旦扛不住,数据库将面临直接裸奔的风险。

需要明确:预热不充分 ≠ 未执行预热,而是预热数据未能覆盖真实热点、未跑完流程、或未能及时更新——这类问题通常在上线后 2–5 分钟内就会引发缓存击穿。
预热脚本需覆盖「实时访问热力图」而非「静态配置文件列表」
许多团队习惯将预热脚本写成读取一个 hot_keys.txt 文件,然后逐条执行 SET 命令。然而,这个文件通常来自上周的离线统计,无法反映新上架商品、突发热搜、运营活动等实时热点。结果就是:当用户大量涌入时,product:102456 这类刚刚爆火的 key 根本不在预热列表内,直接穿透到后端数据库。
- 预热数据源优先级建议:线上实时采样(如 Redis 的
INFO keyspace_hits/keyspace_misses结合客户端埋点 QPS) > 日志聚合(Nginx/应用日志) > 离线报表 - 脚本启动时应自动拉取最近 2 分钟内高 QPS 但未命中的 key(例如命中率低于 0.5 且 QPS 超过 300),并立即加载到缓存中
- 避免硬编码 key 列表;推荐使用动态查询:
redis-cli --scan --pattern "product:*" | xargs -n 100 redis-cli MGET,再结合业务层判断是否为有效热点
预热过程必须包含「分批 + 超时控制」,避免卡死在慢查询上
全量预热中最隐蔽的陷阱,是少数大 value 阻塞了整个预热流程。例如,某个 banner:202605 key 中包含了 base64 编码的图片,读取和写入都很慢,导致后续 key 尚未加载完成,服务就已经对外暴露。这正是预热不充分最容易被忽视的根源。
- 单次批量操作不超过 50 个 key,使用
MGET+MSET减少网络往返次数 - 每批设置 3 秒超时,超时则跳过该批并记录到
/var/log/redis-warmup-skipped.log,便于后续人工介入 - 禁止在预热脚本中调用包含事务、Lua 或 pipeline 的复杂逻辑;仅执行纯数据灌入操作
- 示例关键片段(Shell):
while IFS= read -r key; do timeout 3 redis-cli GET "$key" 2>/dev/null | grep -qE '^[{[]|^[a-zA-Z0-9+/]*={0,2}$' && redis-cli SETEX "$key" 3600 "$(redis-cli GET "$key")"done < <(redis-cli --scan --pattern "product:*" | head -n 50)
预热完成 ≠ 万事大吉,必须校验「缓存命中新鲜度」
脚本执行成功的日志仅表示命令已发出,并不意味着数据真的进入了缓存,或者未被 LRU 策略立即淘汰。常见情况是:预热使用了 SET,但 Redis 配置了 allkeys-lru 且内存紧张,刚写入的数据就被踢出。这才是最令人头疼的“假成功”现象。
- 预热结束后,立即抽样检查 10 个 key 的 TTL:
redis-cli TTL product:10086,如果多数返回-1(永久)或-2(不存在),则说明预热失败 - 对比预热前后
keyspace_hits和keyspace_misses的差值,增量比应 ≥ 90%(即 100 次请求中至少 90 次命中) - 在应用启动后 30 秒内,强制触发一次对预热 key 的模拟访问(例如调用
curl -X GET /api/cache-check?k=product:10086),以验证整个链路是否通畅
真正的难点在于让预热脚本感知业务节奏:秒杀开始前 2 分钟自动加推库存类 key,榜单更新后 10 秒内刷新 top10——这些无法通过固定 cron 定时任务解决。需要将预热能力打造成可由事件驱动的轻量级服务,而不是一个孤零零的 shell 脚本。

