预热时DB被打挂因未限流分片、全量查询触发慢查锁表及连接池耗尽;应按主键分页、加休眠、控制并发、Pipeline分批、设临时标记、渐进切流、拆分Hash结构并监控关键指标。

预热时直接全量查库再塞Redis,为什么DB会瞬间被打挂
因为没做任何限流或分片,SELECT * 一次拉几十万行,还可能触发慢查询、锁表、连接池耗尽。更糟的是,多个服务同时启动预热,DB负载直接翻倍。
- 别用单条
SELECT * FROM table拉全量——改成按主键范围分页,比如WHERE id BETWEEN 1 AND 1000 - 预热脚本里加
time.Sleep(10 * time.Millisecond)(Go)或Thread.sleep(10)(Java),避免请求密度过高 - 确认 DB 连接池最大值(如 MySQL 的
max_connections),预热并发线程数不能超过它的 1/3
用 Redis Pipeline 批量写入但还是卡顿,问题出在哪
Pipeline 虽然减少了网络往返,但如果单次塞太多 key,Redis 主线程处理大包会阻塞,反而拖慢整体进度,还可能触发 OOM command not allowed when used memory > 'maxmemory'。
- 每批 Pipeline 控制在 100–500 个
SET命令之间,用redis.Pipeline().Execute()(Python redis-py)或pipeline.exec()显式提交 - 写入前先用
INFO memory查当前used_memory_rss,接近maxmemory时主动sleep等待淘汰 - 避免在 Pipeline 里混用读命令(如
GET),它会破坏原子性且延长执行时间
预热过程中新请求命中空缓存,DB 又被穿透怎么办
预热还没跑完,用户请求已经进来,而缓存是空的,大量请求直击 DB——这不是缓存没建好,是缓存和业务请求没对齐节奏。
- 预热前先在 Redis 写一个临时标记,比如
SET cache_warming_in_progress "1" EX 3600,业务层检查到这个 key 就返回默认值或降级响应 - 不要等预热全完才切流量,改用渐进式:预热完 10% 数据后,把 10% 流量导到新实例,同时继续预热下一批
- 对关键接口加
GETSET防穿透:先GET,空则SETNX占位 + 异步加载,避免重复打 DB
用 SCAN + HSCAN 分批预热 Hash 结构,为啥越跑越慢
SCAN 本身不阻塞,但配合 HSCAN 嵌套使用时,如果 Hash 字段极多(比如百万级),每次 HSCAN 返回结果少、游标迭代次数爆炸,网络和序列化开销会指数上升。
- 优先用
SSCAN或ZSCAN替代嵌套HSCAN;如果必须用 Hash,提前在 DB 层按业务维度拆成多个小HASH,比如user:profile:shard_001 -
SCAN的COUNT参数不是精确值,设为 1000 比默认 10 更稳,但别设过万,否则单次响应超时风险高 - 别在 Lua 脚本里循环调用
HSCAN——Redis 是单线程,脚本卡住等于整个实例卡住
预热不是“快点把数据倒进去”,而是控制节奏、暴露瓶颈、让 DB 和 Redis 在压力下能呼吸。最容易被忽略的,是预热过程中的监控信号:比如 Redis 的 instantaneous_ops_per_sec 突降、DB 的 Threads_running 持续高于 20,这些比日志里的“success”更有说服力。










