Redis 连接数过高排查与处理

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
喜欢就支持一下吧
点赞6 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容