必须用ristretto而非sync.Map,因其支持自动驱逐与成本控制;查缓存严格先本地后Redis;key需带业务前缀;写操作执行延迟双删并校验版本;Redis客户端须用Failover或Cluster模式并设超时重试。

本地缓存和 Redis 联动不是“加个中间层”就能跑通的事,顺序错、客户端错、key 设错、淘汰策略错,任何一个环节都会让二级缓存变成单点故障或内存泄漏源。
本地缓存必须用 ristretto,别碰 sync.Map
sync.Map 没过期、没容量限制、没淘汰逻辑,上线后冷数据越积越多,GC 压力飙升,高并发读写时反而比 map+RWMutex 还慢。微服务里本地缓存要扛突发、防 OOM、自动驱逐,ristretto 是唯一现实选择。
-
ristretto.NewCache初始化时必须设MaxCost(比如 10 * 1024 * 1024 表示 10MB),靠成本模型主动踢低频条目,不是靠时间 TTL -
Cache.Get()返回(value, ok),其中ok表示“命中本地缓存”,不是“值存在”;未命中时需自行触发回源逻辑 - 本地缓存 TTL 应设为 Redis TTL 的 1/3~1/2(如 Redis 是 30 分钟,本地设 10 分钟),避免本地长期滞留过期数据
- 写操作时,先删 Redis,再删本地(或发消息通知其他实例),不能只删 Redis 留着本地脏数据
Redis 客户端初始化必须用 NewFailoverClient 或 NewClusterClient
硬编码 redis.NewClient 连单节点,在生产环境等于裸奔。它不感知哨兵故障转移,也不自动发现集群新节点,一次网络抖动就卡死在 ctx.Done()。
- 哨兵模式:用
redis.NewFailoverClient,显式传入MasterName和**全部**哨兵地址;漏一个,初始化直接失败 - 集群模式:用
redis.NewClusterClient,只填 2–3 个 seed node 地址即可,但它会在首次连接任一节点失败时 panic,必须包一层if err != nil - 所有客户端都得设
Timeout: 3 * time.Second和MaxRetries: 2,否则抖动时请求堆积,goroutine 持续上涨 -
PoolSize建议设为 10–50:低于 10 容易阻塞,高于 50 可能打满 Redis 的maxclients(默认 10000)
查缓存必须“先本地、再 Redis”,顺序反了就不是二级缓存
顺序颠倒(先 Redis 再本地)本质是降级兜底,本地缓存失去性能价值,还多一层序列化开销。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
立即学习“go语言免费学习笔记(深入)”;
- 典型路径:
localCache.Get(key)→ 命中则返回;未命中则调rdb.Get(ctx, redisKey).Result();Redis 也未命中才查 DB - 回填时,DB 查询成功后先
rdb.Set(...),再localCache.Set(...),保证远程是权威源 - 空值也要缓存:Redis 存
{"empty":true}+ 缩短 TTL(1–2 分钟),本地缓存同样写入空标记,避免穿透 - key 必须带业务前缀和语义分段,如
"user:profile:v2:123",禁用裸 ID(如"123"),防止跨表/跨服务碰撞
缓存更新必须用“延迟双删 + 版本号校验”防不一致
Go 语言本身不提供分布式一致性保障,sync.Map 只保单进程线程安全,跨实例不一致是常态,必须靠外部机制收敛。
- 更新 DB 前先删一次 Redis(清掉可能正在被读的旧值),DB 成功后再删一次 Redis(清掉可能已回源的老值),两次删除之间加
time.Sleep(100 * time.Millisecond) - 读取时从 Redis 解析出 version 字段,对比 DB 当前 version,若落后则丢弃缓存、强制回源
- 更稳妥的做法是用消息队列解耦:DB 提交后发
user_updated消息,独立消费者执行DEL,失败可重试、削峰、解耦 - 所有 key 必须统一编码:结构体走
json.Marshal,别用fmt.Sprintf拼接字符串——后者对空字段、浮点精度、嵌套 map 处理不可靠
真正难的不是“怎么联动”,而是确认“谁负责失效”“谁负责补全”“谁承担不一致窗口”。本地缓存只是加速器,不是数据源;Redis 是共享层,但不是最终权威;DB 才是唯一真相源——这个边界一旦模糊,问题就会从性能退化演变为数据事故。

















