go-redis v9.22 改了跨 SDK 默认值:超时 5s、集群刷新 60s,故障暴露会变慢
v9.22.0 把一批默认值对齐到各 Redis 客户端库共享的提案值。显式设置过这些参数的代码不受影响,依赖旧默认值的服务会直接继承新值。
https://github.com/redis/go-redis/releases/tag/v9.22.0
默认值变化
| 参数 | 旧值 | 新值 |
|---|---|---|
| ReadTimeout / WriteTimeout | 3s | 5s |
| Retry backoff(min / max) | 8ms / 512ms | 10ms / 1s |
| Cluster state reload interval | 10s | 60s |
| TCP keep-alive | 5min 周期 | 30s idle / 5s interval / 3 probes(net.KeepAliveConfig) |
keep-alive 从早期只有一个周期参数,改为 net.KeepAliveConfig 的三项:空闲多久开始探测、探测间隔、连续失败几次判定连接断开。
对现有 Go 服务的影响集中在两点:
- 超时变长会拖慢故障暴露。 单个命令从 3s 才报错变成 5s 才报错,如果上游还有自己的超时和重试预算,叠加后的感知延迟会明显变长。重试退避上限从 512ms 抬到 1s,重试序列整体也更长。
- 集群刷新变慢会拉长故障转移感知。 状态刷新间隔 10s→60s,节点拓扑变化最多要一分钟量级才被客户端发现,期间 MOVED 重定向带来的额外开销也会持续更久。
- keep-alive 反过来更激进:30s 空闲就开始探测,死连接发现更快,代价是探测流量增加。
想维持旧行为就显式设置:
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
ReadTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
MinRetryBackoff: 8 * time.Millisecond,
MaxRetryBackoff: 512 * time.Millisecond,
})
rcl := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{"localhost:7000"},
ClusterStateReloadInterval: 10 * time.Second,
})
Redis 8.10:HIMPORT 与 fieldset
Redis 8.10 支持 HIMPORT,用服务端 fieldset 做批量哈希导入,客户端暴露 HImportPrepare、HImportSet、HImportDiscard、HImportDiscardAll。
fieldset 是绑定在单条物理连接上的会话状态,这跟连接池天然不兼容:prepare 时拿到的可能是 A 连接,真正 SET 时可能落到 B 连接。go-redis 的做法是维护一个带版本的 fieldset 注册表,在任意池化连接上执行需要 fieldset 的 SET 时惰性重放 PREPARE,每条连接最多一次,并且不增加额外往返——PREPARE 被注入到与 SET 同一次写里发出。
HImportPrepare 在连接 C 上建立 fieldset
HImportSet 落到池中任意连接时,若该连接尚未 prepare,
则把 PREPARE 与本次 SET 合并成一次写出
PubSub.Receive 短订阅确认挂死
PeekPushNotificationName 之前会阻塞到缓冲区攒满 36 字节。订阅确认里的频道名不超过 6 个字符时,这个包永远到不了 36 字节,在一条本来就空闲的连接上,PubSub.Receive 会永久挂住。这是 9.20.1 引入的回归,现在改为解析当前已缓冲的内容,不再等固定长度。
集群事务按整事务重路由
此前 MULTI...EXEC 被当作可独立重试的单条命令处理,重试时可能把事务里的命令散到不同节点上。现在 MOVED/ASK/TRYAGAIN 的重定向与中止判断都放在整个事务层面,事务作为整体重新路由并重试。
// 重定向发生时:MULTI...EXEC 整体在新节点重放,
// 而不是把事务内的单条命令各自重试
同一版本还包括 RESP 解析器的加固。