一、Redis连接数基础
1.1 Redis连接模型
Redis采用单线程事件循环模型,所有客户端连接由主线程处理:
## Redis连接处理流程
客户端发起连接请求
↓
Redis主线程accept连接
↓
创建client结构体
├── socket描述符
├── 输入缓冲区(querybuf)
├── 输出缓冲区(buf)
└── 命令队列
↓
注册到epoll事件循环
↓
等待客户端发送命令
↓
命令执行完毕返回结果
1.2 连接数的影响因素
| 因素 | 说明 | 影响 |
|---|---|---|
| maxclients | 最大客户端连接数 | 硬限制 |
| 文件描述符限制 | 系统级fd限制 | 系统瓶颈 |
| 内存使用 | 每个连接占用内存 | 内存瓶颈 |
| CPU性能 | 单线程处理能力 | 性能瓶颈 |
| 网络带宽 | 数据传输速度 | IO瓶颈 |
1.3 每个连接的内存开销
## 单个连接内存消耗估算
- client结构体:约1KB
- 输入缓冲区:默认16KB(动态调整,最大1GB)
- 输出缓冲区:默认16KB(动态调整,三种限制策略)
- 查询缓冲区:根据命令大小
典型连接内存消耗:
- 空闲连接:约2-5KB
- 活跃连接:10-100KB
- 大查询连接:可能达MB级
10000个连接 ≈ 50MB~500MB内存
二、查看当前连接数
2.1 命令行查看
# 连接Redis
redis-cli
# 查看当前连接数
CLIENT LIST
# 返回所有客户端详细信息
# 统计连接数
CLIENT LIST | wc -l
# 查看连接数统计
INFO clients
# 输出示例:
# connected_clients:150
# client_longest_output_list:0
# client_biggest_input_buf:0
# blocked_clients:0
# 查看最大连接数配置
CONFIG GET maxclients
2.2 监控脚本
#!/bin/bash
# Redis连接数监控脚本
REDIS_HOST="127.0.0.1"
REDIS_PORT="6379"
REDIS_CLI="redis-cli -h $REDIS_HOST -p $REDIS_PORT"
# 获取当前连接数
CURRENT=$($REDIS_CLI INFO clients | grep connected_clients | awk -F: '{print $2}' | tr -d '\r')
MAX=$($REDIS_CLI CONFIG GET maxclients | tail -1)
echo "当前连接数: $CURRENT"
echo "最大连接数: $MAX"
echo "使用率: $(( CURRENT * 100 / MAX ))%"
# 查看连接来源分布
echo ""
echo "连接来源分布:"
$REDIS_CLI CLIENT LIST | grep -oP 'addr=\K[^:]+' | sort | uniq -c | sort -rn | head -10
# 查看空闲连接数
echo ""
echo "空闲连接(idle > 60s):"
$REDIS_CLI CLIENT LIST | awk -F' ' '{for(i=1;i<=NF;i++) if($i ~ /^idle=/) print $i}' | awk -F= '{if($2 > 60) print $2}' | wc -l
2.3 实时监控
# 方法一:watch命令
watch -n 1 'redis-cli INFO clients | grep connected_clients'
# 方法二:redis-cli --stat
redis-cli --stat
# 方法三:使用monitor(谨慎,会输出所有命令)
redis-cli MONITOR | grep -E "(CLIENT|CONNECT)"
# 方法四:使用redis-cli --latency监控延迟
redis-cli --latency
三、优化Redis最大连接数
3.1 修改maxclients配置
# 方法一:配置文件修改(永久生效)
sudo nano /etc/redis.conf
# 设置最大连接数
# 默认10000,根据实际需求调整
maxclients 20000
# 如果连接数达到限制,Redis会拒绝新连接并返回错误
# "max number of clients reached"
# 方法二:命令行动态修改(临时生效,重启后失效)
redis-cli CONFIG SET maxclients 20000
# 验证配置
redis-cli CONFIG GET maxclients
3.2 修改系统文件描述符限制
Redis连接数受系统文件描述符限制:
# 查看当前限制
ulimit -n
# 默认通常是1024
# 查看Redis进程的限制
cat /proc/$(pidof redis-server)/limits | grep "open files"
# 临时修改(当前会话有效)
ulimit -n 65535
# 永久修改
sudo nano /etc/security/limits.conf
# /etc/security/limits.conf
# Redis用户
redis soft nofile 65535
redis hard nofile 65535
# 所有用户
* soft nofile 65535
* hard nofile 65535
3.3 修改systemd服务限制
如果Redis由systemd管理,需要修改服务配置:
# 编辑Redis服务文件
sudo systemctl edit redis
[Service]
LimitNOFILE=65535
LimitNPROC=65535
# 重载配置并重启
sudo systemctl daemon-reload
sudo systemctl restart redis
# 验证
cat /proc/$(pidof redis-server)/limits | grep "open files"
3.4 查看Redis启动时的实际限制
# 查看Redis日志
sudo tail -f /var/log/redis/redis.log
# 启动时会输出:
# "Increased maximum number of open files to 10032 (it was originally set to 1024)."
四、连接池配置优化
4.1 为什么需要连接池
## 短连接 vs 长连接
### 短连接(每次请求新建连接)
- 优点:简单,不需要管理连接
- 缺点:
- 每次连接需要TCP三次握手
- 频繁创建/销毁连接消耗资源
- 可能导致连接数飙升
- Redis负载增加
### 长连接(连接池)
- 优点:
- 复用连接,减少握手开销
- 连接数可控
- 性能提升明显
- 缺点:需要管理连接生命周期
4.2 连接池参数配置
Python (redis-py)
import redis
from redis.connection import ConnectionPool
# 创建连接池
pool = ConnectionPool(
host='127.0.0.1',
port=6379,
db=0,
max_connections=100, # 最大连接数
socket_timeout=5, # socket超时(秒)
socket_connect_timeout=5, # 连接超时(秒)
socket_keepalive=True, # 开启TCP keepalive
socket_keepalive_options={
socket.TCP_KEEPIDLE: 60,
socket.TCP_KEEPINTVL: 10,
socket.TCP_KEEPCNT: 3
},
retry_on_timeout=True # 超时重试
)
# 使用连接池
r = redis.Redis(connection_pool=pool)
# 使用完毕后释放连接
# r.connection_pool.release(connection)
Java (Jedis)
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
JedisPoolConfig config = new JedisPoolConfig();
// 最大连接数
config.setMaxTotal(200);
// 最大空闲连接数
config.setMaxIdle(50);
// 最小空闲连接数
config.setMinIdle(10);
// 获取连接最大等待时间(毫秒)
config.setMaxWaitMillis(3000);
// 连接超时时间(毫秒)
config.setConnectTimeout(5000);
// 读取超时时间(毫秒)
config.setReadTimeout(5000);
// 空闲连接检测间隔(毫秒)
config.setTimeBetweenEvictionRunsMillis(30000);
// 连接最小空闲时间(毫秒)
config.setMinEvictableIdleTimeMillis(60000);
// 验证连接有效性
config.setTestOnBorrow(true);
config.setTestOnReturn(false);
config.setTestWhileIdle(true);
JedisPool pool = new JedisPool(config, "127.0.0.1", 6379);
Java (Lettuce)
import io.lettuce.core.RedisClient;
import io.lettuce.core.resource.ClientResources;
import io.lettuce.core.resource.DefaultClientResources;
ClientResources resources = DefaultClientResources.builder()
.ioThreadPoolSize(4) // IO线程池大小
.computationThreadPoolSize(4) // 计算线程池大小
.build();
RedisClient client = RedisClient.create(resources, "redis://127.0.0.1:6379");
Node.js (ioredis)
const Redis = require('ioredis');
const redis = new Redis({
host: '127.0.0.1',
port: 6379,
maxRetriesPerRequest: 3,
enableReadyCheck: true,
enableOfflineQueue: true,
connectTimeout: 10000,
commandTimeout: 5000,
keepAlive: 10000, // TCP keepalive间隔
family: 4, // IPv4
db: 0
});
// 集群模式连接池
const cluster = new Redis.Cluster([
{ host: '127.0.0.1', port: 7000 },
{ host: '127.0.0.1', port: 7001 },
{ host: '127.0.0.1', port: 7002 }
], {
scaleReads: 'slave',
maxRedirections: 16,
retryDelayOnFailover: 1000,
slotsRefreshTimeout: 1000
});
Go (go-redis)
import "github.com/redis/go-redis/v9"
rdb := redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
Password: "",
DB: 0,
PoolSize: 100, // 连接池大小
MinIdleConns: 10, // 最小空闲连接数
MaxIdleConns: 50, // 最大空闲连接数
ConnMaxLifetime: time.Hour, // 连接最大生命周期
ConnMaxIdleTime: time.Minute * 30, // 空闲连接超时
DialTimeout: time.Second * 5, // 连接超时
ReadTimeout: time.Second * 3, // 读超时
WriteTimeout: time.Second * 3, // 写超时
PoolTimeout: time.Second * 4, // 获取连接超时
})
4.3 连接池大小计算公式
## 连接池大小推荐公式
### 基于并发量计算
连接池大小 = 并发请求数 × 平均请求耗时(秒) + 缓冲数
示例:
- 并发请求数:1000 QPS
- 平均请求耗时:5ms = 0.005秒
- 缓冲数:20%
连接池大小 = 1000 × 0.005 × 1.2 = 6
### 基于CPU核心数计算
连接池大小 = CPU核心数 × 2 + 1
示例:8核服务器
连接池大小 = 8 × 2 + 1 = 17
### 经验值推荐
- 小型应用(<100 QPS):10-20
- 中型应用(100-1000 QPS):20-50
- 大型应用(>1000 QPS):50-200
- 超大型应用:200-500(需要分片)
注意:连接池不是越大越好,过大会增加Redis负担
五、连接超时与保活配置
5.1 timeout配置
# redis.conf
# 客户端空闲超时(秒)
# 0表示禁用(不主动断开空闲连接)
# 建议设置300-600秒
timeout 300
# 说明:
# - 客户端空闲超过timeout秒,Redis主动断开连接
# - 可以防止僵尸连接占用资源
# - 但需要客户端有重连机制
5.2 tcp-keepalive配置
# redis.conf
# TCP keepalive设置(秒)
# 0表示禁用
# 建议设置为60-300秒
tcp-keepalive 60
# 说明:
# - Redis定期向客户端发送TCP ACK包
# - 检测客户端是否还存活
# - 发现死连接会主动关闭
# - 对于NAT超时、防火墙超时很有用
5.3 客户端保活配置
# 查看当前keepalive设置
redis-cli CONFIG GET tcp-keepalive
# 动态修改
redis-cli CONFIG SET tcp-keepalive 60
六、连接数异常排查
6.1 连接数突然飙升
# 1. 查看连接来源
redis-cli CLIENT LIST | grep -oP 'addr=\K[^:]+' | sort | uniq -c | sort -rn
# 2. 查看客户端名称和命令
redis-cli CLIENT LIST | grep -oP '(name=\S+|cmd=\S+)'
# 3. 查看空闲连接
redis-cli CLIENT LIST | awk -F' ' '{for(i=1;i<=NF;i++) if($i ~ /^idle=/) print $i}' | sort -t= -k2 -rn | head -20
# 4. 查看大输入缓冲区连接
redis-cli CLIENT LIST | grep -oP 'qbuf=\K[0-9]+' | awk '{if($1>10000) print "Large qbuf: "$1}'
# 5. 查看大输出缓冲区连接
redis-cli CLIENT LIST | grep -oP 'obl=\K[0-9]+' | awk '{if($1>10000) print "Large obl: "$1}'
6.2 清理异常连接
# 1. 查看所有客户端
redis-cli CLIENT LIST
# 2. 杀掉特定客户端(通过ID)
redis-cli CLIENT KILL ID 123
# 3. 杀掉特定IP的连接
redis-cli CLIENT KILL ADDR 192.168.1.100:54321
# 4. 杀掉所有空闲超过60秒的连接
redis-cli CLIENT LIST | awk -F' ' '{
for(i=1;i<=NF;i++) {
if($i ~ /^id=/) id=substr($i,4);
if($i ~ /^idle=/) idle=substr($i,6);
}
if(idle > 60) print id
}' | xargs -I {} redis-cli CLIENT KILL ID {}
# 5. 使用CLIENT KILL过滤器(Redis 6.2+)
redis-cli CLIENT KILL IDLE 60000 # 空闲超过60秒
redis-cli CLIENT KILL TYPE NORMAL # 杀掉普通客户端
redis-cli CLIENT KILL SKIPME yes # 跳过当前连接
6.3 监控脚本(告警)
#!/bin/bash
# Redis连接数告警脚本
REDIS_CLI="redis-cli"
ALERT_THRESHOLD=8000 # 告警阈值
MAX_CLIENTS=$($REDIS_CLI CONFIG GET maxclients | tail -1)
CURRENT=$($REDIS_CLI INFO clients | grep connected_clients | awk -F: '{print $2}' | tr -d '\r')
USAGE_PERCENT=$(( CURRENT * 100 / MAX_CLIENTS ))
if [ "$CURRENT" -gt "$ALERT_THRESHOLD" ]; then
echo "WARNING: Redis连接数过高!"
echo "当前连接数: $CURRENT"
echo "最大连接数: $MAX_CLIENTS"
echo "使用率: ${USAGE_PERCENT}%"
echo ""
echo "连接来源TOP 10:"
$REDIS_CLI CLIENT LIST | grep -oP 'addr=\K[^:]+' | sort | uniq -c | sort -rn | head -10
# 可以在这里添加告警通知(邮件、钉钉、企业微信等)
fi
七、生产环境最佳实践
7.1 推荐配置模板
# ===== Redis连接数优化配置模板 =====
# 最大连接数(根据实际需求调整)
maxclients 20000
# 空闲超时(秒)
timeout 300
# TCP keepalive(秒)
tcp-keepalive 60
# 输出缓冲区限制
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
# 最大输入缓冲区(字节)
client-query-buffer-limit 1gb
# 最大客户端输出缓冲区排队数
max-clients-soft-limit 0 0
7.2 不同规模配置建议
| 规模 | maxclients | timeout | tcp-keepalive | 连接池大小 |
|---|---|---|---|---|
| 小型 | 5000 | 600 | 300 | 10-20 |
| 中型 | 10000 | 300 | 60 | 20-50 |
| 大型 | 20000 | 300 | 60 | 50-100 |
| 超大型 | 50000+ | 300 | 60 | 100-200 |
7.3 客户端最佳实践
## 客户端使用规范
### Do(推荐)
✅ 使用连接池
✅ 设置合理的超时时间
✅ 实现重连机制
✅ 使用连接健康检查
✅ 设置客户端名称(CLIENT SETNAME)
✅ 及时释放不用的连接
### Don't(避免)
❌ 频繁创建/关闭连接
❌ 不设置超时时间
❌ 不处理连接异常
❌ 使用过大的连接池
❌ 忽略连接泄漏
八、总结
Redis连接数优化核心要点:
- 系统限制:调整文件描述符限制(ulimit -n)
- Redis配置:合理设置maxclients和timeout
- 连接池:使用连接池复用连接
- 保活机制:配置tcp-keepalive防止僵尸连接
- 监控告警:实时监控连接数,设置告警阈值
- 异常处理:及时发现和清理异常连接
# 快速检查命令
redis-cli INFO clients
redis-cli CONFIG GET maxclients
redis-cli CLIENT LIST | wc -l
注:本文基于Redis 7.x编写,具体配置请参考对应版本官方文档。