二级缓存必须先查本地再查Redis,顺序不可颠倒;生产环境必须用redis.NewFailoverClient(哨兵)或redis.NewClusterClient(集群),禁用NewClient;需设Timeout、MaxRetries、合理PoolSize,本地缓存优选ristretto而非sync.Map,key须带业务前缀并统一编码。

二级缓存必须先查本地、再查 Redis,顺序反了就不是二级缓存,而是降级兜底——性能优势直接归零。
redis.Client 初始化必须用 NewFailoverClient 或 NewClusterClient
硬编码 redis.NewClient 连单节点,在生产环境等于裸奔。它不感知哨兵故障转移,也不自动发现集群新节点,一次网络抖动就会卡死在 ctx.Done()。真实部署中,90% 的连接超时、Redis 不可用类故障,根子都在这里。
- 哨兵模式:必须用
redis.NewFailoverClient,显式传入MasterName和**全部**哨兵地址;漏一个,初始化直接失败 - 集群模式:用
redis.NewClusterClient,只填 2–3 个 seed node 地址即可,它会自动执行CLUSTER NODES发现其余节点;但首次连任一节点失败,client构造就会 panic,必须包一层if err != nil - 所有客户端都得设
Timeout: 3 * time.Second和MaxRetries: 2,否则抖动时请求堆积,goroutine 持续上涨 -
PoolSize建议设为 10–50,低于 10 容易阻塞,高于 50 可能打满 Redis 的maxclients(默认 10000)
本地缓存别用 sync.Map,优先选 ristretto
sync.Map 没有过期、没容量限制、没淘汰策略,上线后冷数据越积越多,GC 压力大,高并发读场景下写多时性能反而下降。微服务里本地缓存要扛突发流量、自动驱逐、防 OOM,ristretto 是更现实的选择。
-
ristretto默认混合 LFU/LRU,支持带权重的近似容量控制(比如固定 10MB),能主动踢掉低频条目 -
Cache.Get()返回(value, ok),其中ok表示“是否命中缓存”,不是“值是否存在”;未命中时需自行回源并调用Set() - 别封装
sync.Map做简易缓存——没有 TTL 的键会永久滞留,内存增长不可控
缓存 key 设计必须带业务前缀和语义分段
用裸 ID 当 key(如 "123")是线上事故高发点。不同表 ID 碰撞、跨服务覆盖、批量清理失效,全由此起。key 不是标识符,是运维线索。
- 推荐格式:
"user:profile:123"、"order:summary:20260424:uid_789";前缀明确业务域,中间段嵌入时间、租户、版本等维度,便于排查和SCAN过滤 - 所有 key 必须统一编码:结构体走
json.Marshal存 Redis,别用fmt.Sprintf拼接字符串——后者对空字段、浮点精度、嵌套 map 处理不可靠 - 避免在 key 中拼接用户输入(如用户名),需先做
strings.ReplaceAll(username, ":", "_"),防止冒号破坏分段语义 - 若用 Hash 结构(如
HSET user:123 name "Alice" age "30"),记得配套用HGETALL+redis.StringMap解析,别用redis.Strings得到错位数组
读取顺序和空值处理决定缓存是否真有效
常见错误是「查 DB → 写本地 → 写 Redis」,看似稳妥,实则破坏一致性:若第二步失败,本地有新值而 Redis 还是旧值,其他实例读 Redis 就拿到脏数据。空值处理不当,则穿透直接压垮 DB。
- 正确读取顺序:
本地缓存 → Redis → DB;DB 成功后,并行写入本地缓存和 Redis(非串行),失败互不影响 - 查不到时,用
client.Set(ctx, key, "null", 60*time.Second)占位,业务层解包时判if val == "null" { return nil, nil } - 禁止用
EXPIRE单独设置空值过期时间——必须和SET原子执行,否则存在竞态导致空值长期残留 - TTL 别写死:用
baseTTL + time.Duration(rand.Int63n(int64(2*time.Minute)))加扰动,注意每次生成要用独立rand.New(rand.NewSource(time.Now().UnixNano()))实例
真正难的不是“怎么组合本地+Redis”,而是让所有环节对齐语义:key 分段是否可读可运维、序列化是否跨语言一致、空值占位是否可控、本地缓存淘汰是否真的发生——这些细节不落地,二级缓存就只是纸面架构。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











