Redis 连接数过高排查与处理
连接数过高的问题现象
典型症状
1. 应用端:获取连接超时、连接被拒绝
2. Redis 服务端:无法建立新连接
3. 监控告警:connected_clients 超过阈值
查看当前连接数
# 查看连接数
redis-cli INFO clients
# connected_clients:2000
# client_longest_output_list:3
# client_biggest_input_buf:0
# blocked_clients:0
# 查看最大连接数配置
redis-cli CONFIG GET maxclients
# 1) "maxclients"
# 2) "10000"
# 查看系统级限制
ulimit -n
# 65536
排查步骤
第一步:确认连接数状态
# 1. 基础连接统计
redis-cli INFO clients
# 2. 按客户端类型分组
redis-cli CLIENT LIST
# id=1234 addr=10.0.0.1:54321 fd=8 ...
# id=1235 addr=10.0.0.2:54322 fd=9 ...
# 3. 统计各 IP 的连接数
redis-cli CLIENT LIST | awk '{print $2}' | cut -d= -f2 | cut -d: -f1 | sort | uniq -c | sort -nr
# 500 10.0.0.1 ← 某 IP 有 500 个连接
# 300 10.0.0.2
# 200 10.0.0.3
第二步:分析连接类型
# 查看连接的详细状态
redis-cli CLIENT LIST | head -5
# id=1234 addr=10.0.0.1:54321 fd=8 name=app-server-1 age=3600 idle=10 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 argv-mem=10 obl=0 oll=0 omem=0 tot-mem=61466 events=r cmd=get
# ↑ ↑ ↑ ↑ ↑ ↑ ↑
# ID 地址 描述名 存活时间 空闲时间 数据库 当前命令
# 分析空闲连接
redis-cli CLIENT LIST | awk -F 'idle=' '{print $2}' | awk '{print $1}' | sort -n | tail -10
# 3600 ← 空闲 1 小时的连接
# 7200 ← 空闲 2 小时的连接
第三步:检查应用端连接池
# 以 Java Jedis 为例,检查连接池配置
# 典型错误配置示例
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(2000); # 单实例连接池过大
config.setMaxIdle(1000); # 空闲连接过多
config.setMinIdle(500); # 最小空闲连接过高
config.setMaxWaitMillis(3000); # 获取连接超时
# 如果部署了 10 个应用实例
# 总连接数 = 2000 × 10 = 20000 个连接
常见原因分析
原因 1:连接池配置不合理
# 问题示例
应用实例数: 20
每个实例连接池: maxTotal=500
理论最大连接: 20 × 500 = 10000
# 合理配置
应用实例数: 20
每个实例连接池: maxTotal=50 # 根据 QPS 重新评估
理论最大连接: 20 × 50 = 1000
原因 2:连接泄漏
// 连接泄漏的典型代码
public void badCode() {
Jedis jedis = pool.getResource();
jedis.get("key");
// 忘记 jedis.close() / jedis 归还到连接池
}
// 正确的做法
public void goodCode() {
try (Jedis jedis = pool.getResource()) {
jedis.get("key");
} // 自动归还
}
原因 3:短连接风暴
# 每次请求创建新连接,用完不复用
# 瞬时大量请求 → TCP 连接数飙升
# 监控指标
redis-cli INFO stats
# total_connections_received:100000 ← 总接收连接数增长过快
# rejected_connections:10 ← 被拒绝的连接数
原因 4:慢查询导致连接堆积
# 慢查询导致请求排队,连接不释放
redis-cli SLOWLOG GET 10
# 1) 1) (integer) 1
# 2) (integer) 1700000000
# 3) (integer) 50000 ← 执行 50ms 的慢查询
# 4) 1) "KEYS"
# 2) "user:*"
原因 5:发布/订阅连接积压
# Pub/Sub 订阅者消费慢
redis-cli INFO clients
# client_longest_output_list:1000 ← 输出缓冲区积压严重
处理方案
即时处理
# 1. 增加最大连接数(临时方案)
redis-cli CONFIG SET maxclients 20000
# 同时修改系统限制
sysctl -w net.core.somaxconn=65535
# 2. 杀掉空闲连接
redis-cli CLIENT LIST | awk '$8 ~ /idle=/' | while read line; do
idle=$(echo $line | grep -oP 'idle=\K\d+')
id=$(echo $line | grep -oP 'id=\K\d+')
if [ "$idle" -gt 300 ]; then # 空闲超过 5 分钟
redis-cli CLIENT KILL ID $id
fi
done
# 3. 杀掉特定应用的连接
redis-cli CLIENT KILL ADDR 10.0.0.1:6379
redis-cli CLIENT KILL TYPE master
redis-cli CLIENT KILL SKIPME yes
长期解决方案
# 连接池配置优化(以 Java 为例)
# 应用端配置建议:
maxTotal: 50 # 最大连接数(根据 QPS 评估)
maxIdle: 20 # 最大空闲连接
minIdle: 5 # 最小空闲连接
maxWaitMillis: 5000 # 获取连接超时(防死锁)
testOnBorrow: true # 获取时验证
testWhileIdle: true # 空闲时验证
timeBetweenEvictionRunsMillis: 30000 # 30s 清活一次
连接管理工具
# 监控和自动清理脚本
def clean_idle_connections(redis_conn, max_idle_seconds=300):
"""清理空闲连接"""
clients = redis_conn.client_list()
cleaned = 0
for client in clients:
idle = int(client['idle'])
client_id = int(client['id'])
if idle > max_idle_seconds:
try:
redis_conn.client_kill_filter(id=client_id)
cleaned += 1
except:
pass
return cleaned
# 定时执行
# crontab: */5 * * * * python3 clean_connections.py
预防措施
1. 监控告警配置
# Prometheus 告警规则
- alert: RedisHighConnections
expr: redis_connected_clients > redis_maxclients * 0.8
for: 5m
annotations:
summary: "Redis 连接数超过 maxclients 的 80%"
- alert: RedisConnectionRejected
expr: rate(redis_rejected_connections_total[5m]) > 0
annotations:
summary: "Redis 拒绝新连接"
2. 容量规划
# 连接数规划公式
预计峰值连接数 = 应用实例数 × 连接池最大大小 × (1 + 冗余)
= 50 × 50 × 1.5 = 3750
# 配置 maxclients
maxclients = 3750 × 1.5 = 5600 # 预留余量
3. 应用层限流
// 应用层限流,避免突发连接
public class RedisRateLimiter {
private final Semaphore semaphore = new Semaphore(50);
public void executeWithLimit(Runnable task) {
if (semaphore.tryAcquire(100, TimeUnit.MILLISECONDS)) {
try {
task.run();
} finally {
semaphore.release();
}
} else {
throw new TooManyRequestsException();
}
}
}
面试要点
- 排查三步走:INFO clients → CLIENT LIST → 应用端连接池
- 常见原因:连接池过大、连接泄漏、短连接风暴、慢查询
- 即时处理:增大 maxclients、清理空闲连接
- 长期方案:合理配置连接池、连接复用、监控告警
- maxclients ≠ ulimit:Redis maxclients + 一些内部连接 ≤ 系统 ulimit -n
- 关注 rejected_connections:这个计数器 >0 说明已经拒绝过连接
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END


暂无评论内容