Skip to content

Redis 客户端连接与故障处理 ​

本页面向需要把 Redis 接入生产应用的后端开发人员,说明如何选择客户端能力、设置连接生命周期,并处理超时、故障转移和集群重定向。内容以 Redis 7.4.11 为基准;客户端库的配置名称不同,但需要解决的问题相同。

连接管理的目标不是让连接永远不断,而是在故障发生时有界地等待、准确地判断结果,并在能力允许时恢复。权威来源见Redis 客户端处理说明、Sentinel 客户端规范和 Redis Cluster 规范。

1. 确定连接拓扑 ​

客户端必须知道应用连接的是单实例、Sentinel 还是 Cluster。三者对地址发现、读写位置和重连行为的要求不同。

拓扑客户端必须具备的能力故障后的关键动作
单实例连接地址、超时、连接池、健康检查有界重连;超过错误预算后快速失败
Sentinel使用 Sentinel 地址和服务名发现主节点重新查询主节点,验证角色,不复用旧主地址
Cluster维护槽到节点的映射,处理重定向和拓扑刷新跟随 MOVED,按规范处理 ASK,刷新槽映射

不要把服务器 IP 写死后用于 Sentinel 服务。Sentinel 规范要求客户端在每次重连时重新解析服务地址,并通过 ROLE 等命令确认目标确实是主节点。Cluster 客户端收到 MOVED 后应向指定节点重发查询;槽迁移期间收到 ASK 时,应按客户端协议完成 ASKING 后再发送原命令。

多 Key 命令还要先检查拓扑约束:

  • 单实例和 Sentinel 不限制不同 Key 的槽位置。
  • Cluster 中的多 Key 操作必须位于同一哈希槽;相关 Key 使用一致的哈希标签。
  • 读副本时要接受复制延迟,写请求必须由客户端或代理路由到可写主节点。

机制分别见哨兵和集群。

2. 选择客户端能力 ​

选型时先验证故障行为,再比较 API 是否顺手。至少检查以下能力:

  • 支持目标部署形态,尤其是 Sentinel 服务发现或 Cluster 槽路由。
  • 能分别设置连接、读取和写入超时,而不是只有一个模糊的总超时。
  • 连接池具有最大连接数、空闲连接和等待队列上限,能够暴露等待和拒绝指标。
  • 重试次数有上限,退避带随机抖动,并能限制一段时间内的总重试预算。
  • 能刷新拓扑,不把一次连接失败或 MOVED 当作永久地址变化。
  • 提供连接数、池等待时间、命令延迟、错误类型和拓扑刷新事件。
  • 支持应用使用的 RESP 版本、认证、TLS 和所需命令。

RESP 3 是 Redis 6 引入的协议版本,能返回更丰富的回复类型;RESP 2 仍是许多客户端和部署的默认兼容路径。选择协议前应确认服务端、代理和客户端三者一致,不要仅根据协议版本判断客户端优劣。

客户端选型应通过一次重启、一次 Sentinel 故障转移和一次 Cluster 槽迁移演练验证。只用 PING 成功作为验收条件,会漏掉最重要的拓扑刷新和重连问题。

3. 设置连接生命周期 ​

不同客户端对配置的命名不同,但都应覆盖以下生命周期。

配置作用配置过大的风险配置过小的风险
连接超时限制建立 TCP 连接的等待时间故障时请求积压网络抖动时误判成功
读取超时限制等待响应的时间故障恢复变慢,线程或协程被占用高峰期把慢请求当作失败
连接池上限限制同时持有的连接打满服务端 maxclients 和文件描述符池等待时间成为主要延迟
最大空闲时间回收长期不用的连接保留过多无用连接频繁建连和 TLS 握手
重试上限限制单次操作的额外尝试故障被放大,恢复变慢短暂抖动直接暴露给调用方

服务端 timeout 只会在空闲达到指定秒数后断开客户端连接,不能代替客户端读取超时。TCP keepalive 用于探测连接是否仍然存在,也不能保证应用已经收到响应。

连接池大小应从应用并发和池等待指标推导,不直接套用“每 100 QPS 一个连接”之类的固定公式。所有客户端的池上限、监控连接和管理连接之和,应低于服务端 maxclients,并为突发流量保留余量:

text
Σ(应用连接池上限) + 监控/管理连接 + 协议开销 < maxclients × 安全水位

还应检查进程和操作系统的文件描述符上限。服务端在无法接受新连接时会返回连接数超限错误并立即关闭连接;此时增加客户端重试只会提高服务端压力。

4. 限制重试和请求放大 ​

重试前要区分“请求肯定没有执行”和“结果未知”。Redis 返回超时只说明客户端没有及时收到响应,不表示服务器没有执行命令。

结果是否可直接重试原因与处理
尚未发送请求通常可以按退避策略重连并重新发送
已发送,响应未知需要业务判断INCR、LPUSH、XADD 等命令可能已经成功
NOAUTH、WRONGPASS、NOPERM不应重试先修复认证或 ACL 配置
CROSSSLOT不应重试修改 Key 分组或哈希标签
MOVED、ASK由集群客户端处理更新路由后按协议重发,不手工无限循环
READONLY重新发现拓扑写请求此前被发到了只读副本
LOADING有界重试仅在业务允许的恢复窗口内退避等待
OOM command not allowed默认不重试先确认容量、淘汰策略和业务降级

对结果未知且不可重复的写入,应由业务使用幂等键、结果核对或补偿流程处理。客户端无法替应用判断一次 INCR 到底执行了零次、一次还是两次。

重试采用指数退避并加入随机抖动,同时满足三个边界:

  1. 单次操作的总等待时间不超过业务超时。
  2. 单位时间内的总重试量不超过故障预算。
  3. 连续失败达到阈值后打开熔断,让调用方快速失败或走降级路径。

连接池耗尽时,等待本身也是延迟。池等待应有独立上限;不能在应用线程中无限等待一个永远不会恢复的 Redis。

5. 区分常见连接故障 ​

先按错误类型分类,再决定重试、修复配置或切换拓扑。

现象优先检查处理方向
连接被拒绝服务状态、监听地址、防火墙修复网络或路由,不提高重试次数
连接建立后很快断开maxclients、空闲超时、代理空闲策略调整连接预算和空闲参数
认证失败ACL 用户名、密码注入、默认用户状态轮换错误凭据,确认实际登录身份
权限不足ACL Key 模式和命令规则按最小权限补规则,不直接授予全部命令
写请求返回 READONLYSentinel/Cluster 拓扑缓存重新发现可写节点并刷新连接
延迟和池等待同时升高阻塞命令、大 Key、池容量查看慢日志和连接池指标
集群持续重定向槽映射、网络地址公布、代理配置检查 CLUSTER NODES 和客户端拓扑刷新

具体错误见常见错误与连接问题。排障时同时观察应用侧指标和服务端的 INFO clients、CLIENT LIST、SLOWLOG,不要只看进程是否存活。

6. 观察连接状态 ​

应用侧至少记录:

  • 连接成功、失败和当前活跃连接数。
  • 连接池最大连接数、空闲连接数、等待任务数和等待时间。
  • 连接、读取、重试和拓扑刷新各自的耗时与失败次数。
  • 按错误类型区分的结果,不把所有失败合并成一个“Redis 异常”。
  • 故障转移前后旧连接的释放速度和新拓扑的生效时间。

服务端可从 INFO clients 查看 connected_clients、blocked_clients 和 maxclients。CLIENT LIST 用于查看单个连接的地址、空闲时间和发送请求数;连接很多时应限制输出规模,并避免把包含应用名称或地址的日志长期留存到不受控位置。

连接相关指标的告警不应只针对连接总数。更常见的故障信号是池等待时间上升、重试率上升、拓扑刷新失败,以及连接数达到上限前的持续增长。

7. 验证连接方案 ​

上线前至少完成以下测试:

  1. 拒绝一个错误地址,确认连接超时受控,调用方没有无上限等待。
  2. 重启 Redis,确认连接池清理失效连接,新连接能够恢复。
  3. 执行 Sentinel 故障转移,确认客户端重新发现主节点,不继续向旧地址写入。
  4. 执行 Cluster 槽迁移,确认 MOVED 和 ASK 被正确处理,应用无持续重定向循环。
  5. 切换 ACL 凭据,确认旧连接失效后按计划重建,新凭据不写入日志。
  6. 制造高并发池等待,确认等待超时、降级和告警符合预期。

验收记录应包含业务超时、池上限、重试预算、故障恢复时间,以及每项测试的预期结果。没有经过故障演练的默认配置,只能证明连接成功,不能证明故障可控。

8. 参考资料 ​

Released under the MIT License.