跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

Go语言如何做分布式缓存_Go语言分布式缓存教程【干货】

Go用Redis做分布式缓存需配置连接池、带超时context、空值占位防穿透、TTL扰动防雪崩、慎用本地缓存;细节决定生产可用性。 Go 用
redis
做分布式缓存,别直接上
github.com/go-redis/redis/v9
就完事 很多 Go 项目一说“分布式缓存”,立刻拉
go-redis/v9
,配个
redis.NewClient
,写两行
Set
/
Get
就上线。结果压测时连接数暴涨、超时频发、缓存击穿没兜底——不是库不行,是没处理好连接复用和上下文生命周期。
redis.NewClient
默认不启用连接池复用,每个请求都可能新建连接;必须显式配置
&redis.Options{PoolSize: 20}
(根据 QPS 和 Redis 实例规格调) 所有
Get
/
Set
调用必须带
context.Context
,且不能传
context.Background()
这种永不过期的上下文;建议统一用
ctx, cancel := context.WithTimeout(r.Context(), 500*time.Millisecond)
别在 handler 里反复
client.Set(ctx, key, val, ttl)
—— 如果
val
是结构体,先
json.Marshal
再存;Redis 不认 Go 类型,直接传 struct 会存成空字符串 缓存穿透:查不到的 key 频繁打到 DB,
go-redis
怎么兜住 用户查一个永远不存在的
user_id=999999999
,缓存没命中,DB 也没数据,下次还来——这就是穿透。靠 Redis 自身解决不了,得在 Go 层加一层“空值占位”逻辑。 查询前先
client.Get(ctx, key)
,如果返回
redis.Nil
,说明缓存里真没有;这时不要直接查 DB,而是先
client.Set(ctx, key, "null", time.Minute)
(注意值是字符串
"null"
,不是
nil
) 后续请求再读到这个
"null"
,就跳过 DB 直接返回空响应;但要注意:业务层解包时得判断
if val == "null" { return nil, nil }
,否则会把字符串当真实数据 别用
SETNX
手动实现布隆过滤器——Go 生态有现成的
github.com/yourbasic/bloom
,但小项目没必要,空值缓存 + 短 TTL 更简单可控 缓存雪崩:大量 key 同时过期,DB 瞬间被打垮,
time.Now().Add
别写死 所有缓存都设
10 * time.Minute
,整点一到,全崩。这不是 Redis 的问题,是 Go 代码里没做过期时间扰动。 设置 TTL 时别直接写死:
client.Set(ctx, key, val, 10*time.Minute)
;改成
baseTTL := 10 * time.Minute; jitter := time.Duration(rand.Int63n(int64(2*time.Minute))) ; client.Set(ctx, key, val, baseTTL+jitter)
别在全局 init 或变量初始化阶段生成随机数种子——Go 的
rand
默认用固定 seed,每次重启后 jitter 都一样;应该在每次 Set 前调
rand.New(rand.NewSource(time.Now().UnixNano()))
或用
math/rand
的线程安全变体
rand.New(rand.NewSource(time.Now().UnixNano())).Int63n(...)
如果用了 Redis Cluster,还要注意:不同 slot 的 key 过期时间扰动要独立计算,不能共用一个 jitter 变量 本地缓存 + 分布式缓存双写不一致,
sync.Map
不是银弹 有人想“加速热点 key”,在 Go 进程内加一层
sync.Map
,读缓存先查本地,查不到再查 Redis,写的时候两个地方一起更新。听起来合理,实际极易出错。 go语言参考手册 中文CHM版 Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文给大家带来Go参考手册,需要的可以来下载! Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、v数组安全、编译 下载 立即学习 “ go语言免费学习笔记(深入) ”;
sync.Map
适合读多写少,但一旦涉及“删除”或“更新”,本地 Map 和 Redis 很难严格同步;比如 A 服务删了 Redis 的 key,B 服务本地 Map 还缓着旧值,就会返回脏数据 更稳妥的做法是只用 Redis,但给热点 key 加
GETEX
(Redis 6.2+)或用
EVAL
脚本保证原子读+续期;或者用
github.com/dgraph-io/ristretto
这类带 TTL 和淘汰策略的本地 cache 库,它支持基于 LFU 的自动驱逐,比手写
sync.Map
安全得多 如果真要用双写,务必加版本号或时间戳字段,读时校验本地缓存的
version
是否等于 Redis 中的
version
,不等就丢弃本地值并重载 分布式缓存真正难的从来不是“怎么连上 Redis”,而是怎么让多个 Go 实例在失效、穿透、雪崩、并发更新这些边界条件下,行为可预测、数据不越界。参数、上下文、时间扰动、空值标记——这些细节堆起来,才构成生产可用的缓存逻辑。

相关文章