下面按事前预防、事中处置、事后复盘治理三个阶段给你一套可以直接落地的方案。
这里的报错 ERR max number of clients reached 在互联网生产中最常见于 Redis 连接数达到 maxclients 上限,也可能出现在 Redis Proxy、云 Redis、缓存集群代理层,但本质都是:服务端可接受的客户端连接数已经耗尽,新连接被拒绝。
一、问题本质
1. 报错含义
Redis 返回:
ERR max number of clients reached
表示当前 Redis 实例的客户端连接数已经达到上限:
connected_clients >= maxclients
常见表现:
- 应用访问 Redis 报错;
- 缓存读写失败;
- 登录、鉴权、会话、验证码、库存、限流等依赖 Redis 的功能异常;
- 大量线程阻塞在获取 Redis 连接;
- 应用连接池耗尽;
- 请求 RT 飙升;
- 错误率升高;
- 如果降级不充分,可能进一步压垮数据库或下游服务。
2. 典型根因
线上出现该问题,一般不是单点原因,而是以下一种或多种叠加:
2.1 应用侧连接泄漏
例如:
- 每次请求都创建 Redis 客户端;
- Redis 连接没有关闭或归还连接池;
- 异常路径没有释放连接;
- 协程、线程、Future 超时后连接未释放;
- pipeline、transaction、pub/sub 连接没有关闭;
- 应用热部署后老连接未释放。
2.2 连接池配置不合理
例如 Java Jedis、Lettuce、Redisson、Go Redis、Node Redis 等客户端配置不当:
- 每个应用实例连接池过大;
- 最大连接数无限制;
- 空闲连接不回收;
- borrow 等待时间过长;
- 没有连接获取超时;
- HPA 扩容后实例数翻倍,Redis 总连接数随之翻倍;
- 每个业务模块都创建一个独立 Redis 客户端,连接数成倍增加。
2.3 HPA、弹性扩容、发布导致连接风暴
例如:
- 大促流量上来后应用自动扩容;
- 每个 Pod 启动时建立大量连接;
- 一次性滚动发布大量实例;
- 启动探针、预热逻辑大量访问 Redis;
- 服务短时间重启,旧连接未及时释放,新连接又打上来。
2.4 Redis 服务端配置不足
例如:
maxclients 10000
但实际业务总连接预算已经超过 10000。
同时 Redis 的 maxclients 还受操作系统文件描述符限制影响:
ulimit -n
Redis 启动时如果 OS 文件描述符太小,实际可用 maxclients 会被限制。
2.5 慢命令、阻塞命令导致连接堆积
如果 Redis 执行慢:
KEYS- 大 Key 操作
- 大批量
MGET - 大 Lua 脚本
SORTSMEMBERS大集合LRANGE大列表HGETALL大 Hash- 阻塞命令,如
BLPOP、BRPOP
则客户端请求处理慢,连接长时间占用,最终连接数上升。
2.6 网络异常导致连接残留
例如:
- 应用到 Redis 网络抖动;
- NAT Gateway 连接异常;
- SLB、Proxy 半连接残留;
- 客户端未感知连接断开;
- TCP keepalive 配置不合理;
- Kubernetes 网络组件异常。
2.7 Pub/Sub、Stream 消费者连接膨胀
Pub/Sub 和阻塞消费通常会长期占用连接:
- 消费者数量无限增长;
- 每个订阅主题一个连接;
- 没有统一连接复用;
- 消费者异常退出后连接残留。
2.8 多租户或多业务混用 Redis
一个 Redis 被多个业务共用:
- A 业务突增连接;
- B 业务被连带影响;
- 没有按照业务重要性隔离;
- 没有连接数预算和配额。
二、事前:预防体系建设
事前目标是:即使业务增长、发布、扩容、异常流量、代码缺陷出现,也不会让 Redis 连接数耗尽,或者能够在耗尽前自动发现、自动限流、自动降级。
1. 架构设计阶段
1.1 Redis 不能无限共享,要做业务隔离
互联网大厂生产中不建议所有业务共用一个 Redis。
应该按照以下维度拆分:
| 维度 | 建议 |
|---|---|
| 核心链路 | 登录、支付、交易、库存、风控单独隔离 |
| 非核心链路 | 推荐、埋点、活动、排行榜可独立集群 |
| 读写压力 | 高 QPS 读缓存和写密集型业务拆开 |
| 数据模型 | 普通 KV、计数器、队列、Session、分布式锁分开 |
| 故障影响面 | 一个业务连接异常不能拖垮其他业务 |
最低要求:
- 核心业务 Redis 独立集群;
- 高风险业务 Redis 独立集群;
- 测试、预发、生产完全隔离;
- 不同业务有独立账号、ACL、监控、配额。
1.2 建立 Redis 连接数容量模型
所有接入 Redis 的系统必须做连接数预算。
公式:
Redis 总连接数 =
Σ(服务实例数 × 每实例 Redis 最大连接数 × Redis 客户端数量)
+ 运维连接
+ 监控连接
+ Proxy 连接
+ Sentinel/Cluster 连接
+ Pub/Sub/Stream 长连接
+ 预留 Buffer
例如:
一个业务有 5 个服务:
| 服务 | 实例数 | 每实例连接池最大连接数 | Redis client 数 | 最大连接数 |
|---|---|---|---|---|
| order-service | 100 | 20 | 1 | 2000 |
| user-service | 80 | 20 | 1 | 1600 |
| activity-service | 50 | 30 | 2 | 3000 |
| risk-service | 60 | 10 | 1 | 600 |
| job-service | 20 | 50 | 1 | 1000 |
业务最大连接数:
2000 + 1600 + 3000 + 600 + 1000 = 8200
再加:
监控/运维/复制/哨兵/Proxy/预留 buffer
如果按 30% 预留:
8200 × 1.3 = 10660
那么 Redis maxclients 至少要大于 10660,并且要评估 CPU、内存、网络是否支撑。
1.3 为每个 Redis 实例设置连接数红线
建议分三级:
| 指标 | 阈值 | 动作 |
|---|---|---|
| 提醒 | 60% maxclients | 观察趋势 |
| 警告 | 70% maxclients | 通知业务负责人 |
| 严重 | 80% maxclients | 触发限流/排查 |
| 紧急 | 90% maxclients | 自动降级/禁止扩容/人工介入 |
| 故障 | 100% | 新连接失败 |
不要等到 100% 才报警。
1.4 Redis 服务端参数要纳入基线
必须统一基线化 Redis 配置。
重点参数:
maxclients 30000
timeout 300
tcp-keepalive 300
说明:
maxclients
控制 Redis 最大客户端连接数。
查看:
CONFIG GET maxclients
INFO clients
修改:
CONFIG SET maxclients 30000
持久化:
CONFIG REWRITE
注意:
如果系统文件描述符限制不够,Redis 无法真正支撑这么多连接。
timeout
关闭空闲连接。
timeout 300
表示普通客户端空闲 300 秒后断开。
注意:
- 不建议设成过小,否则长时间空闲但合法的连接可能被频繁断开;
- Pub/Sub、阻塞命令、复制等场景要谨慎;
- 应结合客户端连接池的 idle 配置。
tcp-keepalive
用于探测死连接。
tcp-keepalive 300
一般建议生产开启,避免网络异常后连接长期残留。
1.5 操作系统文件描述符必须匹配
Redis 的每个客户端连接都需要文件描述符。
检查:
ulimit -n
检查 Redis 进程限制:
cat /proc/$(pidof redis-server)/limits
systemd 配置:
[Service]
LimitNOFILE=100000
Redis 启动时如果文件描述符限制不足,日志中通常会有类似提示:
You requested maxclients of 30000 requiring at least 30032 max file descriptors.
Server can't set maximum open files to 30032 because of OS error.
生产要求:
LimitNOFILE >= maxclients + Redis内部文件描述符 + 安全余量
一般建议 Redis 进程文件描述符至少:
maxclients + 10000
1.6 不能盲目调大 maxclients
调大 maxclients 前必须评估:
| 资源 | 风险 |
|---|---|
| 内存 | 每个连接有客户端结构、输入缓冲、输出缓冲 |
| CPU | 连接数增加会增加事件循环负担 |
| 网络 | 大量客户端并发会打满网卡 |
| fd | 文件描述符耗尽 |
| 延迟 | Redis 单线程处理命令,连接多不等于吞吐高 |
| 输出缓冲 | 慢客户端可能导致内存暴涨 |
要避免:
把 maxclients 从 10000 直接调到 100000,却不治理应用连接池
这只会把问题从连接数耗尽变成 Redis 延迟飙升或 OOM。
2. 应用开发阶段
2.1 所有 Redis 客户端必须统一封装
大厂生产中不允许业务代码随意创建 Redis Client。
必须通过统一 SDK 或基础组件访问 Redis:
- 统一连接池配置;
- 统一超时配置;
- 统一重试策略;
- 统一埋点;
- 统一降级;
- 统一 client name;
- 统一接入治理平台。
禁止:
// 错误示例:每次请求创建客户端
Jedis jedis = new Jedis("redis-host", 6379);
jedis.get("key");
正确方式:
// 应使用全局单例连接池
JedisPool jedisPool = new JedisPool(config, host, port);
try (Jedis jedis = jedisPool.getResource()) {
return jedis.get(key);
}
2.2 Redis Client 必须是单例或受控连接池
每个应用进程内:
- Redis Client 不要重复创建;
- 每个 Redis 集群最多一个主 Client;
- 不同 Redis 集群使用不同 Client;
- 不同用途可以分池,但必须登记连接预算。
禁止:
- 每个 DAO 一个 RedisClient;
- 每个请求 new RedisClient;
- 每个租户 new RedisClient;
- 每个线程 new RedisClient;
- 每个任务 new RedisClient。
2.3 连接池必须有上限
以 Java Jedis 为例:
redis.pool.maxTotal=20
redis.pool.maxIdle=10
redis.pool.minIdle=2
redis.pool.maxWaitMillis=50
redis.timeout=100
redis.soTimeout=100
关键点:
| 参数 | 要求 |
|---|---|
| maxTotal | 必须设置,不能无限 |
| maxIdle | 不能过大 |
| minIdle | 不要盲目设置太高 |
| maxWaitMillis | 必须设置,不能无限等待 |
| command timeout | 必须设置 |
| socket timeout | 必须设置 |
| connect timeout | 必须设置 |
如果连接池拿不到连接,应该快速失败或降级,而不是一直阻塞。
2.4 每个服务必须有连接池预算
例如:
order-service:
Redis maxTotal: 20
最大实例数: 100
最大连接预算: 2000
这个预算必须进入服务元数据:
service: order-service
redis:
cluster: cache-order-prod
max_pool_size_per_instance: 20
max_instance_count: 100
max_connection_budget: 2000
发布、扩容、HPA 前必须校验连接预算是否超限。
2.5 HPA 扩容必须受 Redis 连接预算约束
例如:
当前 Redis 可用连接预算:
maxclients = 30000
当前连接数 = 18000
剩余安全连接数 = 30000 × 0.8 - 18000 = 6000
如果某服务每个 Pod 最大连接数 30:
最多还能新增 Pod = 6000 / 30 = 200
HPA 最大副本数不能超过这个限制。
必须避免:
流量上升 -> HPA 扩容 -> Redis 连接数飙升 -> Redis 拒绝连接 -> 应用错误率升高 -> HPA 继续扩容
这是典型的正反馈雪崩。
2.6 应用必须设置 Redis clientName
每个连接要能从 Redis 侧识别来源。
Redis 支持客户端设置名称:
CLIENT SETNAME order-service@pod-xxx
客户端接入层要统一设置:
服务名 + 环境 + 实例ID + 机房 + 版本
例如:
order-service|prod|az-a|pod-123|v20240501
这样事中排查可以通过:
CLIENT LIST
看到是谁占用了连接。
2.7 所有 Redis 调用必须设置超时
必须包括:
| 超时 | 建议 |
|---|---|
| connect timeout | 50ms ~ 200ms |
| read timeout | 50ms ~ 300ms |
| command timeout | 根据业务 SLA |
| pool borrow timeout | 10ms ~ 100ms |
不要无限等待。
错误示例:
Redis 命令卡住 -> 业务线程卡住 -> 连接不释放 -> 连接池耗尽 -> 实例继续创建连接 -> Redis maxclients 达到上限
2.8 Redis 调用必须有熔断和降级
不是所有 Redis 失败都应该拖垮业务。
按业务类型处理:
| 类型 | Redis 失败处理 |
|---|---|
| 普通缓存 | 回源 DB,但要加限流和防击穿 |
| 非核心推荐 | 返回默认值 |
| 排行榜 | 返回旧数据 |
| 风控 | 进入保守策略 |
| 分布式锁 | 失败则不执行关键操作 |
| 限流器 | 进入本地限流兜底 |
| Session | 核心链路需要多活或备用方案 |
一定要避免:
Redis 不可用 -> 所有请求直接打 DB -> DB 被打崩
所以 Redis 降级必须和 DB 保护一起设计。
2.9 禁止高风险 Redis 命令
线上业务禁止或严格审批:
KEYS *
FLUSHALL
FLUSHDB
MONITOR
CLIENT KILL 无限制执行
HGETALL 大 Hash
SMEMBERS 大 Set
LRANGE 超大范围
ZRANGE 超大范围
复杂 Lua 脚本
推荐替代:
| 禁止/慎用 | 替代 |
|---|---|
| KEYS | SCAN |
| HGETALL 大 Hash | 分页 HSCAN |
| SMEMBERS 大 Set | SSCAN |
| LRANGE 0 -1 | 分页 |
| 大 Key | 拆分 Key |
| 大 Lua | 拆小或异步化 |
慢命令会导致连接占用时间变长,间接推高 connected_clients。
3. 测试阶段
3.1 单元测试必须覆盖连接释放
重点测试:
- 正常路径连接释放;
- 异常路径连接释放;
- 超时路径连接释放;
- 取消任务时连接释放;
- pipeline 执行异常后释放;
- Lua 异常后连接释放;
- pub/sub 取消订阅后释放。
Java 中必须检查:
try-with-resources
finally
Go 中检查:
defer close
context timeout
Node.js 中检查:
client.quit()
client.disconnect()
3.2 集成测试检查连接池行为
测试场景:
| 场景 | 验证 |
|---|---|
| 并发请求 | 连接数不超过 maxTotal |
| Redis 宕机 | 不无限创建连接 |
| Redis 慢响应 | 连接池不被永久占满 |
| 请求超时 | 连接能释放 |
| 应用重启 | 老连接能断开 |
| 发布滚动 | 连接数平滑变化 |
必须在测试中观察 Redis:
INFO clients
CLIENT LIST
3.3 压测必须包含连接数指标
压测不能只看 QPS 和 RT,必须看:
| 指标 | 说明 |
|---|---|
| connected_clients | 当前连接数 |
| rejected_connections | 被拒绝连接数 |
| blocked_clients | 阻塞客户端 |
| total_connections_received | 累计连接数 |
| Redis CPU | CPU 使用率 |
| Redis 内存 | 包括 client buffer |
| 网络流量 | 入/出流量 |
| 命令耗时 | slowlog、latency |
压测通过标准必须包含:
峰值流量 × 1.5 或 2 倍下,
connected_clients < maxclients × 70%
rejected_connections = 0
Redis P99 延迟满足 SLA
3.4 长稳测试必须做
很多连接泄漏短压测看不出来。
至少做:
12 小时、24 小时、72 小时长稳测试
重点观察:
connected_clients 是否持续单调递增
total_connections_received 是否异常增长
应用连接池 active 是否不释放
如果连接数持续上涨但流量稳定,基本可以判断存在连接泄漏或连接未复用。
3.5 故障演练必须包含 Redis 连接耗尽
演练场景:
- Redis
maxclients调小模拟连接耗尽; - 应用连接池耗尽;
- Redis 慢命令导致连接堆积;
- 大量 Pod 同时启动;
- 网络抖动导致连接残留;
- Proxy 层连接耗尽;
- HPA 扩容导致 Redis 连接暴涨。
验证:
- 告警是否及时;
- 自动降级是否触发;
- SRE 是否能定位来源服务;
- 是否能通过
CLIENT LIST找到异常连接; - 是否能快速恢复;
- 是否会压垮 DB。
4. 发布阶段
4.1 发布前必须做 Redis 连接预算校验
每次发布前检查:
服务当前实例数
发布后最大实例数
每实例 Redis 最大连接池
Redis 当前连接数
Redis maxclients
剩余安全容量
例如:
当前 connected_clients = 15000
maxclients = 30000
安全阈值 = 80% = 24000
剩余安全连接数 = 9000
本次发布服务最大新增连接数 = 4000
允许发布
如果:
本次发布服务最大新增连接数 > 剩余安全连接数
必须阻断发布。
4.2 灰度发布必须观察连接数
灰度阶段看:
- 新版本每实例连接数是否高于老版本;
- 是否存在连接持续增长;
- total connections 是否异常增长;
- Redis rejected_connections 是否出现;
- 应用连接池 active 是否异常;
- 客户端错误率是否升高。
灰度门禁:
新版本单实例 Redis 连接数不超过基线 20%
rejected_connections = 0
connected_clients 无持续上升趋势
4.3 滚动发布要限制并发批次
不要一次重启大量实例。
建议:
maxUnavailable <= 5%
maxSurge <= 5% ~ 10%
并且根据 Redis 连接容量调整。
避免:
1000 个 Pod 同时启动,每个 Pod 建 20 个连接,瞬间新增 20000 个连接
4.4 应用启动要做连接预热限速
启动时不要瞬间把连接池打满。
建议:
- 懒加载连接;
- 连接池按需增长;
- 预热有速率限制;
- 启动后延迟接流量;
- readiness probe 通过前不要接收业务流量;
- 控制同时启动实例数量。
5. 运维监控阶段
5.1 Redis 侧必须监控的指标
通过:
INFO clients
INFO stats
INFO commandstats
INFO memory
SLOWLOG GET
LATENCY DOCTOR
核心指标:
| 指标 | 含义 | 告警建议 |
|---|---|---|
| connected_clients | 当前连接数 | > 70%、80%、90% 分级 |
| maxclients | 最大连接数 | 配置基线 |
| rejected_connections | 拒绝连接累计数 | 增量 > 0 立即告警 |
| blocked_clients | 阻塞客户端数 | > 0 持续告警 |
| total_connections_received | 累计连接数 | 斜率异常告警 |
| client_recent_max_input_buffer | 客户端输入缓冲峰值 | 异常增大告警 |
| client_recent_max_output_buffer | 客户端输出缓冲峰值 | 异常增大告警 |
| used_memory | 内存使用 | 常规监控 |
| used_memory_rss | RSS | 常规监控 |
| instantaneous_ops_per_sec | QPS | 趋势判断 |
| latest_fork_usec | fork耗时 | RDB/AOF 风险 |
| slowlog len | 慢日志 | 增长告警 |
重点:
rejected_connections 只要出现增量,就说明已经出过连接拒绝
这应该是严重告警。
5.2 应用侧必须监控连接池指标
每个服务必须上报:
| 指标 | 含义 |
|---|---|
| redis_pool_active | 活跃连接数 |
| redis_pool_idle | 空闲连接数 |
| redis_pool_waiters | 等待连接线程数 |
| redis_pool_borrow_time | 获取连接耗时 |
| redis_pool_create_count | 新建连接数 |
| redis_pool_destroy_count | 销毁连接数 |
| redis_cmd_timeout_count | Redis 命令超时数 |
| redis_cmd_error_count | Redis 错误数 |
| redis_cmd_rt_p95/p99 | Redis 命令耗时 |
| redis_cmd_qps | Redis 命令 QPS |
必须能按以下维度聚合:
- 服务名;
- 实例;
- 机房;
- Redis 集群;
- 命令类型;
- 版本号。
5.3 必须能从 Redis 反查来源服务
要求所有客户端设置 CLIENT SETNAME。
排查命令:
CLIENT LIST
示例字段:
id=123 addr=10.1.2.3:45678 name=order-service|prod|pod-xxx age=3600 idle=0 db=0 cmd=get
通过 name 和 addr 可以定位:
- 哪个服务;
- 哪个 Pod;
- 哪个版本;
- 哪个机房;
- 是否异常连接。
5.4 建立 Redis 连接数看板
至少包含:
- Redis 集群总连接数;
- 各 Redis 节点连接数;
- maxclients;
- 使用率;
- rejected_connections 增量;
- top client name;
- top client addr;
- 应用连接池 active;
- 应用实例数;
- HPA 副本数;
- 版本发布时间线;
- Redis 慢命令;
- Redis CPU、内存、网络。
看板必须能回答:
哪个服务在什么时候把连接数打上去了?
是发布导致,扩容导致,还是流量导致?
连接数上涨后有没有释放?
三、事中:线上故障应急处理
事中目标是:先止血恢复业务,再定位根因,避免扩大影响。
1. 故障识别
出现以下任一情况,进入 Redis 连接耗尽应急流程:
- 应用日志出现:
ERR max number of clients reached
- Redis 指标:
connected_clients >= maxclients × 90%
rejected_connections出现增量;- 应用 Redis 连接池等待线程数飙升;
- 大面积 Redis timeout;
- 业务错误率升高。
2. 立即拉起应急角色
互联网大厂建议明确分工:
| 角色 | 职责 |
|---|---|
| Incident Commander | 整体指挥、决策 |
| Redis SRE | Redis 侧排查和操作 |
| 应用 owner | 应用侧降级、回滚、限流 |
| 业务负责人 | 业务影响判断 |
| DBA | 防止 DB 被打爆 |
| 网关负责人 | 流量控制 |
| 发布负责人 | 暂停发布或回滚 |
3. 第一优先级:止血
3.1 暂停相关发布、扩容、批任务
立即执行:
- 暂停当前发布;
- 暂停自动扩容;
- 暂停大批量任务;
- 暂停离线任务访问 Redis;
- 暂停非核心消费者;
- 暂停压测或活动流量;
- 冻结配置变更。
尤其要防止:
HPA 持续扩容导致连接数继续上涨
3.2 启动业务降级
按业务优先级降级:
| 业务 | 动作 |
|---|---|
| 推荐、排行榜、非核心缓存 | 返回默认值或旧值 |
| 活动页 | 降级静态页 |
| 批处理任务 | 暂停 |
| 低优先级查询 | 限流 |
| 登录/支付/交易 | 保留最小 Redis 能力 |
| DB 回源 | 必须加限流 |
注意:
Redis 故障时不能无脑走 DB,否则会造成二次事故。
3.3 网关或服务层限流
如果已经影响核心链路,需要限流保护。
可以按:
- 接口;
- 用户等级;
- 来源渠道;
- 地域;
- 业务优先级;
- 非核心服务;
- 爬虫和异常流量。
优先限:
非核心、高 QPS、低价值、可重试、批量类接口
4. 第二优先级:确认 Redis 状态
如果还能连上 Redis,执行:
INFO clients
CONFIG GET maxclients
INFO stats
INFO memory
INFO commandstats
SLOWLOG GET 20
CLIENT LIST
重点查看:
connected_clients
blocked_clients
maxclients
rejected_connections
total_connections_received
示例:
redis-cli INFO clients
输出可能类似:
connected_clients:9999
client_recent_max_input_buffer:20480
client_recent_max_output_buffer:1048576
blocked_clients:0
查看上限:
redis-cli CONFIG GET maxclients
4.1 如果 Redis 已经无法新建连接怎么办?
因为连接已经满了,新的 redis-cli 也可能连不上。
可选手段:
- 使用已有的运维连接或监控连接执行命令;
- 登录 Redis 所在机器本地尝试连接;
- 如果有 Proxy 管理面,使用管理面;
- 通过云厂商控制台查看指标和调整参数;
- 如果之前预留了管理通道,使用管理通道;
- 必要时先从应用侧切断异常来源,等待连接回落。
事前建议长期保持一个低频、受控的运维管理连接,但不要依赖它作为唯一手段。
5. 第三优先级:定位谁占用了连接
5.1 按客户端名称统计
执行:
CLIENT LIST
拿到连接列表后按 name、addr、cmd 聚合。
如果客户端都设置了名称,可以统计:
order-service 2000
activity-service 12000
risk-service 500
unknown 3000
如果没有设置 client name,则只能按 IP 聚合:
10.1.2.3 300
10.1.2.4 500
10.1.2.5 2000
这会显著增加排查成本,所以事前必须强制设置 clientName。
5.2 看连接是否空闲
CLIENT LIST 中关注:
| 字段 | 含义 |
|---|---|
| age | 连接存在时间 |
| idle | 空闲时间 |
| cmd | 最后执行命令 |
| db | 使用的库 |
| name | 客户端名 |
| addr | 客户端地址 |
| obl/oll/omem | 输出缓冲相关 |
| qbuf/qbuf-free | 输入缓冲相关 |
如果大量连接:
idle 很大
cmd=get/set
说明可能是空闲连接池过大或连接泄漏。
如果大量连接:
cmd=blpop/brpop/xread
说明可能是阻塞消费连接多。
如果大量连接:
cmd=subscribe
说明可能是 Pub/Sub 连接膨胀。
如果大量连接:
omem 很大
说明可能有慢客户端或大响应导致输出缓冲堆积。
6. 第四优先级:释放连接
6.1 优先从应用侧回收或缩容
如果定位到某服务连接异常,优先做:
- 回滚最近版本;
- 降低连接池上限;
- 缩容异常服务;
- 停止异常任务;
- 关闭非核心功能;
- 重启异常实例释放连接。
注意:
缩容、重启要分批进行,避免重启后又瞬间建更多连接。
6.2 Redis 侧谨慎 kill 连接
如果需要 Redis 侧释放连接,可以使用:
CLIENT KILL
例如按地址:
CLIENT KILL ADDR 10.1.2.3:45678
按客户端名称需要根据 Redis 版本能力决定,常见方式是先 CLIENT LIST 找到 id,再 kill:
CLIENT KILL ID 12345
杀普通客户端:
CLIENT KILL TYPE normal
注意:
不要误杀:
- replicas;
- sentinel;
- cluster bus;
- 正在执行核心业务的连接;
- 运维连接;
- Proxy 关键连接。
实操建议:
- 先 kill 明确异常来源;
- 先 kill 长时间 idle 的连接;
- 分批 kill;
- 每批后观察业务错误率和连接数;
- 不要一次性无差别 kill。
6.3 临时设置 timeout 回收空闲连接
如果大量连接空闲,可以临时调整:
CONFIG SET timeout 60
这会让普通空闲客户端在 60 秒后断开。
风险:
- 可能影响合法长空闲连接;
- 对 Pub/Sub、阻塞连接不一定适用;
- 要与应用连接池 idle 策略配合;
- 故障恢复后应恢复到标准值。
6.4 临时调大 maxclients
如果 Redis 资源充足,这是快速止血手段之一。
步骤:
第一步:查看当前
CONFIG GET maxclients
INFO clients
第二步:查看 OS fd 限制
cat /proc/$(pidof redis-server)/limits
第三步:评估资源
确认:
- Redis CPU 未打满;
- Redis 内存有余量;
- 网络未打满;
- fd 足够;
- 没有严重慢命令;
- 连接数上涨是短期峰值而不是持续泄漏。
第四步:临时调整
CONFIG SET maxclients 30000
第五步:确认生效
CONFIG GET maxclients
INFO clients
第六步:决定是否持久化
如果确认长期需要:
CONFIG REWRITE
如果只是临时止血,不要随意持久化,事后走容量评审。
6.5 如果 OS fd 不足
如果 LimitNOFILE 太小,单纯 CONFIG SET maxclients 不一定生效。
需要修改 systemd:
[Service]
LimitNOFILE=100000
然后重启 Redis 才能彻底生效。
但是线上故障中重启 Redis 风险很高,必须评估:
- 是否有主从;
- 是否有哨兵;
- 是否有 Redis Cluster;
- 是否可 failover;
- 是否允许短暂不可用;
- 数据是否有持久化;
- 当前业务是否能承受。
不建议故障中盲目重启 Redis。
7. 第五优先级:确认是否有慢命令导致连接堆积
查看慢日志:
SLOWLOG GET 20
查看延迟:
LATENCY LATEST
LATENCY DOCTOR
查看命令统计:
INFO commandstats
如果发现:
- 大量
keys; - 大量大范围
zrange/lrange; - 大量
hgetall/smembers; - Lua 执行时间长;
- 网络输出很大;
需要立即:
- 禁用相关接口;
- 降级相关功能;
- 回滚发布;
- kill 执行异常命令的客户端;
- 后续做数据结构治理。
8. 第六优先级:恢复验证
恢复不能只看错误率下降,还要确认:
connected_clients 降到安全阈值以下
rejected_connections 不再增长
blocked_clients 正常
Redis P99 延迟正常
应用 Redis 连接池 active/idle 正常
业务错误率恢复
DB 未被回源打爆
HPA 没有继续异常扩容
建议恢复标准:
connected_clients < maxclients × 70%
rejected_connections 10 分钟无新增
业务错误率回到基线
Redis P99 延迟回到基线
四、事后:复盘、修复、治理
事后目标:找到真实根因,修代码、修机制、修监控、修流程,确保同类问题不会再次发生。
1. 复盘必须收集的数据
1.1 时间线
记录:
故障开始时间
首次告警时间
业务受影响时间
人工介入时间
止血动作时间
恢复时间
根因确认时间
要回答:
为什么没有更早发现?
为什么没有自动止血?
为什么影响范围这么大?
1.2 Redis 指标
收集故障前后:
- connected_clients;
- maxclients;
- rejected_connections;
- total_connections_received;
- blocked_clients;
- Redis CPU;
- Redis memory;
- network in/out;
- slowlog;
- latency;
- commandstats;
- client list 快照;
- top client name;
- top client addr。
1.3 应用指标
收集:
- 实例数变化;
- HPA 扩容记录;
- 发布时间;
- 版本变化;
- 连接池 active/idle/wait;
- Redis 请求量;
- Redis 错误率;
- Redis 超时;
- 线程池使用率;
- GC;
- 应用重启记录;
- 业务接口 RT 和错误率。
1.4 变更记录
排查故障前是否有:
- 代码发布;
- 配置变更;
- Redis 参数变更;
- 扩容;
- 活动上线;
- 定时任务启动;
- 数据迁移;
- 网络变更;
- 云厂商维护;
- 业务流量突增。
2. 根因分类
根因必须归类,不能只写:
Redis 连接数过高
这不是根因。
应该明确到以下粒度:
2.1 代码问题
例如:
activity-service v1.2.3 中每次请求创建 RedissonClient,未复用连接池,导致单实例连接数从 20 上升到 300。
2.2 配置问题
例如:
JedisPool maxTotal 从 20 被误改为 200,服务有 150 个实例,理论连接上限从 3000 增加到 30000。
2.3 扩容问题
例如:
HPA maxReplicas 设置为 1000,未纳入 Redis 连接预算,流量高峰时扩容导致连接数翻倍。
2.4 Redis 容量问题
例如:
Redis maxclients 10000,实际业务峰值连接预算已经达到 16000,容量评估缺失。
2.5 慢命令问题
例如:
新版本上线后增加 HGETALL 大 Hash,单次响应达到数 MB,导致客户端输出缓冲堆积,连接释放变慢。
2.6 监控缺失
例如:
只监控 Redis QPS 和 CPU,未监控 connected_clients 使用率和 rejected_connections 增量。
2.7 流程问题
例如:
发布平台未校验 Redis 连接预算,新服务接入 Redis 未经过容量评审。
3. 修复动作
3.1 修应用代码
必须检查:
- Redis Client 是否单例;
- 是否每请求创建连接;
- 所有异常路径是否释放连接;
- 是否存在异步任务不释放连接;
- pipeline 是否关闭;
- pub/sub 是否退订并关闭;
- 是否无限重试;
- 是否连接失败后疯狂重连;
- 是否没有超时;
- 是否重复创建多个 RedisTemplate、RedissonClient、JedisPool。
3.2 修连接池配置
统一配置模板。
示例:
redis:
pool:
maxTotal: 20
maxIdle: 10
minIdle: 2
maxWaitMillis: 50
timeout:
connect: 100
read: 100
command: 100
retry:
maxAttempts: 1
circuitBreaker:
enabled: true
不同业务可调整,但必须评审。
3.3 修 HPA 策略
HPA 要增加外部约束:
- Redis 连接数使用率高于 80%,禁止继续扩容;
- Redis rejected_connections 出现,触发降级而不是扩容;
- 应用错误率因 Redis 导致时,不应盲目扩容;
- 最大副本数受连接预算约束。
3.4 修发布平台门禁
发布前自动检查:
当前 Redis connected_clients
Redis maxclients
服务当前实例数
服务发布后实例数
每实例 Redis maxTotal
新增连接风险
最近 30 分钟 rejected_connections
最近 30 分钟 Redis P99
不满足条件则阻断发布。
3.5 修监控告警
必须新增:
connected_clients / maxclients > 70% warning
connected_clients / maxclients > 80% critical
connected_clients / maxclients > 90% emergency
rejected_connections increase > 0 critical
total_connections_received 斜率异常 warning
blocked_clients > 0 warning
client output buffer 异常 warning
应用连接池 waiters > 0 critical
应用获取 Redis 连接耗时 P99 上升 critical
3.6 修 Redis 容量
如果确认为容量不足:
- 调大 maxclients;
- 调整 OS fd;
- 增加 Redis 分片;
- 拆分业务 Redis;
- 引入独立读写集群;
- 拆分 Pub/Sub;
- 拆分 Stream 消费;
- 对高连接业务增加 Proxy 或本地缓存;
- 对 Serverless、短连接场景做连接复用或连接代理。
3.7 修数据结构和慢命令
如果连接堆积由慢命令引起:
- 清理大 Key;
- 拆分大 Hash、大 Set、大 List;
- 禁止全量读取;
- 分页 Scan;
- Lua 限制执行复杂度;
- 增加命令审计;
- 上线前做 key size 检查;
- 对大 Value 压缩或拆分;
- 对热点 Key 做本地缓存或多级缓存。
4. 建立长期治理机制
4.1 Redis 接入审批
新服务接入 Redis 必须提交:
业务场景
Redis 集群
预计 QPS
峰值 QPS
实例数
HPA 最大实例数
每实例连接池大小
连接预算
Key 设计
Value 大小
TTL 策略
降级方案
压测报告
负责人
未经审批不得接入生产 Redis。
4.2 Redis 连接数配额
每个服务分配连接配额:
order-service: 3000
user-service: 2000
activity-service: 5000
超过配额告警。
如果使用 Redis ACL,可结合用户维度做统计和治理。
4.3 强制统一客户端 SDK
SDK 必须内建:
- 单例管理;
- 连接池上限;
- 超时;
- 熔断;
- 降级;
- 指标上报;
- clientName;
- 慢命令日志;
- 大 key 风险拦截;
- 发布版本打标。
4.4 定期容量巡检
每周或每月巡检:
Redis connected_clients 峰值
maxclients 使用率
rejected_connections 是否出现过
top client
服务实例数变化
HPA maxReplicas
未来活动容量
大促容量
慢命令
大 Key
输出容量报告。
4.5 大促前专项检查
大促、活动、春晚、秒杀前必须检查:
- Redis maxclients;
- OS fd;
- 应用连接池;
- HPA 上限;
- 降级开关;
- 限流开关;
- Redis 集群容量;
- 连接数压测;
- 故障演练;
- 应急 runbook;
- 负责人值班表。
4.6 建立标准 Runbook
Runbook 内容:
- 如何确认连接耗尽;
- 如何查看 Redis connected_clients;
- 如何查看 maxclients;
- 如何查看 rejected_connections;
- 如何执行 CLIENT LIST;
- 如何定位 top client;
- 如何 kill 异常连接;
- 如何临时调大 maxclients;
- 如何回滚应用;
- 如何降级;
- 如何限流;
- 如何恢复;
- 如何复盘。
Runbook 要演练,不是写完放文档。
五、最终检查清单
你可以拿下面清单逐项对照现状。
事前检查清单
| 检查项 | 是否完成 |
|---|---|
| Redis 是否按业务隔离 | |
| 核心业务是否独立 Redis | |
| 每个服务是否有 Redis 连接预算 | |
| HPA 是否纳入 Redis 连接预算 | |
Redis maxclients 是否满足峰值需求 | |
OS LimitNOFILE 是否足够 | |
是否设置合理 timeout 和 tcp-keepalive | |
| 应用是否统一 Redis SDK | |
| Redis Client 是否单例 | |
| 是否禁止每请求创建连接 | |
| 连接池是否设置 maxTotal | |
| 连接池是否设置 maxWait | |
| Redis 命令是否设置超时 | |
| 是否有熔断降级 | |
| 是否设置 clientName | |
| 是否监控 connected_clients | |
| 是否监控 rejected_connections | |
| 是否监控应用连接池 active/idle/wait | |
| 是否有 top client 看板 | |
| 是否做过长稳测试 | |
| 是否做过连接耗尽演练 | |
| 发布平台是否校验连接预算 | |
| 灰度是否观察连接数 | |
| 大促前是否做专项容量评估 |
事中处理清单
| 步骤 | 动作 |
|---|---|
| 1 | 确认是否 ERR max number of clients reached |
| 2 | 暂停发布、扩容、批任务 |
| 3 | 启动业务降级和限流 |
| 4 | 查看 INFO clients |
| 5 | 查看 CONFIG GET maxclients |
| 6 | 查看 rejected_connections |
| 7 | 执行 CLIENT LIST |
| 8 | 按 clientName/IP 找 top client |
| 9 | 回滚或缩容异常应用 |
| 10 | 分批 kill 明确异常连接 |
| 11 | 必要时临时调大 maxclients |
| 12 | 检查慢命令和 blocked_clients |
| 13 | 观察连接数和业务错误率恢复 |
| 14 | 确认 DB 未被回源打爆 |
事后治理清单
| 检查项 | 是否完成 |
|---|---|
| 是否形成完整时间线 | |
| 是否保存 Redis 指标快照 | |
| 是否保存 CLIENT LIST 快照 | |
| 是否确认根因服务 | |
| 是否确认根因版本 | |
| 是否修复连接泄漏 | |
| 是否修复连接池配置 | |
| 是否补充监控 | |
| 是否补充告警 | |
| 是否补充发布门禁 | |
| 是否补充压测场景 | |
| 是否补充故障演练 | |
| 是否更新 Runbook | |
| 是否复盘到机制问题 | |
| 是否明确长期 owner | |
| 是否设置整改截止时间 |
六、核心原则总结
ERR max number of clients reached不是简单调大maxclients就能彻底解决的问题。- 必须同时治理:应用连接池、HPA、发布、监控、Redis 服务端容量、慢命令、降级限流。
- 每个服务必须有 Redis 连接预算。
- 所有 Redis 客户端必须统一封装、单例复用、设置 clientName。
rejected_connections增量必须作为严重告警。- 发布和扩容必须校验 Redis 剩余连接容量。
- 事中优先止血,避免 Redis 故障扩散到 DB 和核心链路。
- 事后必须把问题沉淀成平台化门禁、监控、Runbook 和容量治理机制。

737

被折叠的 条评论
为什么被折叠?



