预热必须在main()中同步执行,严格按InitDB→InitRedis→preloadCache→http.ListenAndServe()顺序;禁用init()和goroutine,需分页查必要字段、pipeline写入Redis,并设降级标记与验证机制。

预热必须在 main() 里同步执行,不能放 init() 或 goroutine
Go 服务启动时缓存预热失败,八成是执行时机错了。放在 init() 函数里?DB 连接池还没建、redis.Client 还是 nil,一调 client.Get() 就 panic;用 go preloadCache() 异步跑?http.ListenAndServe() 返回后主 goroutine 退出,预热中途被强制终止,日志只显示 “loaded 312/5000”,Redis 里数据残缺不全。
正确顺序只能是:InitDB() → InitRedis() → preloadCache() → http.ListenAndServe()。所有依赖必须显式初始化完毕、可安全调用,预热函数才允许执行。
- 禁用任何包级
init()做预热逻辑 - 若用了 Wire 等 DI 框架,确保
preloadCache是显式调用的函数,不是自动 resolve 的“服务” - 真要控制耗时,用
context.WithTimeout包裹整个预热流程,超时后记 warn 日志并设降级标记(如atomic.StoreBool(&cacheWarmedUp, false))
从 MySQL 加热点数据到 Redis,怎么避免拖垮数据库?
直接 SELECT * FROM product WHERE is_hot = 1 是线上事故高发操作:大表触发深分页、锁表、OOM;冗余字段让 Redis 内存暴涨 3 倍。预热不是 dump 全量,而是按访问热度和缓存容量精筛。
- 只查必要字段:
SELECT id, name, price, updated_at FROM product WHERE status = 1 ORDER BY pv DESC LIMIT 10000 - 用游标分页替代
OFFSET:WHERE id > ? ORDER BY id LIMIT 100,每次取上一批最后id继续 - 读之前显式设置事务隔离:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ,确保看到一致快照 - 加覆盖索引:WHERE 条件和 SELECT 字段都在同一索引里,避免回表
Redis 写入必须走 pipeline,每批 ≤1000 条
单条 SET 写几万条缓存,会拖慢启动、触发 Redis 连接超时。必须用 client.Pipeline() 批量提交,但别误用 client.TxPipeline() —— 预热不需要事务原子性,TxPipeline 会额外加锁阻塞其他 goroutine。
立即学习“go语言免费学习笔记(深入)”;
- 每批不超过 1000 条:太大触发
client-output-buffer-limit溢出,报错却是ERR Client sent AUTH, but no password is set这类误导信息 - 记得调用
pipe.Exec(ctx),漏掉等于白写 - 跨 Pod 防重复:启动前先用
SETNX cache_warmup_lock 1 EX 30抢锁,成功才执行,失败就跳过或重试 -
sync.Once只防单进程内重复,K8s 多 Pod 场景下无效
本地缓存选 sync.Map 还是 bigcache?看规模和过期需求
sync.Map 和 bigcache 都线程安全,但适用场景差异明显:前者适合万级以内 key、读多写少、无需过期;后者适合十万级以上、需内存可控、能接受少量 GC 开销。
- 别用原生
map替代sync.Map—— 并发写直接 panic - 别用
sync.Map存带 TTL 的数据 —— 它不支持过期,得自己配定时器清理,极易漏删 - 别用
bigcache存结构体 —— 它只认[]byte,必须提前序列化,反序列化失败会静默丢数据 - 本地分片函数必须稳定输出:
func localShardForKey(key string, shardCount int) int { h := crc32.ChecksumIEEE([]byte(key)); return int(h) % shardCount },避免用hash/maphash默认构造(seed 随进程随机)
预热逻辑看似简单,真正难的是时机卡点、DB 压力控制、Redis 批量写入稳定性、以及跨实例去重——这些地方没做对,轻则缓存不全,重则服务启动失败或 DB 被打挂。


















