首页 / Redis 入门教程 / 连接池与性能调优

Redis 入门教程

连接池与性能调优

本教程共 40 篇 · 第 35 篇 · 更新于 2026-08-02

连接池性能调优timeoutmaxclients阻塞

35. 连接池与性能调优

前两章把 Java、Python 客户端的「怎么连、怎么用」讲清楚了,这一章码上学把注意力放到「连得好不好、快不快」上。Redis 是单线程处理命令的,客户端侧的任何浪费——频繁建连、慢查询、超大输出——都会被放大成全局延迟。掌握连接池与本节列出的调优项,才能让你的应用真正发挥 Redis 8.x 的吞吐优势。

提示:先用 redis-cli 看一下与服务端连接相关的几个关键配置,它们正是调优的旋钮。

127.0.0.1:6379> CONFIG GET timeout
1) "timeout"
2) "0"
127.0.0.1:6379> CONFIG GET tcp-keepalive
1) "tcp-keepalive"
2) "300"
127.0.0.1:6379> CONFIG GET maxclients
1) "maxclients"
2) "10000"

本节目标

  • 理解为什么必须复用连接(连接池),以及连接建立的成本到底高在哪。
  • 能配置 Jedis 的连接池参数(最大/空闲连接、等待超时),并理解 redis-py 的内建池。
  • 掌握服务端 timeouttcp-keepalivemaxclientsmaxmemory-clients 各自控制什么。
  • 识别并规避「阻塞单线程」的性能红线:大 key、KEYS、慢脚本、超大输出缓冲区。
  • 会用 INFO clientsSLOWLOGredis-cli --bigkeys 做连接与性能诊断。

一、为什么需要连接池

每一次客户端连接 Redis,都要经历 TCP 三次握手、可能的 TLS 协商、Redis 协议握手(HELLO/AUTH/SELECT)这一系列开销。如果「来一个请求就新建一个连接、用完就关」,这些握手成本会吃掉大量时间,而且瞬时高并发时还会把 maxclients 迅速打满。

连接池的思路是:预先建立一批连接放在池里,需要时借出、用完归还,而不是销毁。这样绝大多数请求都跳过了建连开销,连接数也始终受池大小约束。

Jedis 连接池

第 33 章用的是「每次 new 一个 Jedis」的写法,适合演示;生产环境一定要用 JedisPool

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(64);        // 池中最多 64 个连接
config.setMaxIdle(16);         // 最多保留 16 个空闲连接
config.setMinIdle(4);          // 至少预热 4 个空闲连接
config.setMaxWaitMillis(2000); // 借不到连接时最多等 2 秒,超时抛异常

try (JedisPool pool = new JedisPool(config, "localhost", 6379)) {
    try (Jedis jedis = pool.getResource()) {   // 从池里借
        jedis.set("k", "v");
        System.out.println(jedis.get("k"));
    }                                          // 归还连接,不是关闭
}

几个参数怎么定:maxTotal 要大于应用的并发线程数,但别大到把服务端 maxclients 压垮;maxIdle 让空闲时也有热连接可用,减少突发时的建连;maxWaitMillis 是保护项,取不到连接就快速失败,避免线程无限堆积。

redis-py 的内建池

第 34 章提到过,redis-py 的 Redis 对象默认就带连接池,你也可以显式控制大小:

import redis

pool = redis.ConnectionPool(
    host='localhost', port=6379,
    decode_responses=True,
    max_connections=16
)
r = redis.Redis(connection_pool=pool)

max_connections 默认几乎是「不限制」,在连接数可能失控的场景(如 serverless、短生命周期函数)显式限制会更安全。

二、服务端的连接相关配置

redis.conf 里有几个旋钮直接决定连接的生死与资源占用:

  • timeout 0:客户端空闲多少秒后服务端主动断开它。0 表示永不因空闲断开。如果你的客户端用了长连接池,保持 0 没问题;但如果客户端连接泄漏、从不释放,把 timeout 设成比如 300(5 分钟)可以让服务端自动清理「半死不活」的连接,避免 maxclients 被占满。
  • tcp-keepalive 300:每隔 300 秒发送 TCP keepalive 探测,用来识别已经挂掉的对端(比如客户端机器突然断电),及时释放其连接与文件描述符。公网或不太稳定的网络环境下建议保持开启。
  • tcp-backlog 511:TCP 全连接队列长度,高并发建连时适当调大可避免握手请求被丢弃。
  • maxclients 10000:最大客户端数,受操作系统文件描述符限制(详见第 32 章)。

客户端输出缓冲区限制 maxmemory-clients

这是 Redis 8.x(最新稳定版)里尤其要关注的一项。每个客户端在接收服务端回复时,服务端会为其分配一块输出缓冲区暂存数据。如果某个客户端消费得很慢(比如订阅了海量 Pub/Sub 消息、或者一次性查询返回了超大结果),这块缓冲区就会无限膨胀,吃掉内存甚至拖垮整个实例。

maxmemory-clients 用来给「所有客户端输出缓冲区」设上限,超出后 Redis 会主动驱逐(断开)最占内存的客户端:

127.0.0.1:6379> CONFIG GET maxmemory-clients
1) "maxmemory-clients"
2) "5%"

默认值通常是 5%(即 maxmemory 的 5%,若未设 maxmemory 则按可用内存估算)。也可以写成绝对大小,例如 maxmemory-clients 1g。对 Pub/Sub 或大数据返回场景较多的业务,合理设置它能防止「一个慢消费者拖垮全站」。

三、性能红线:别阻塞单线程

Redis 顺序执行命令,一次只处理一条。任何「耗时命令」都会让排在后面的所有客户端一起卡住。调优的核心就是别制造慢命令

  1. 避免大 key。一个存了上百万成员的 Set、或几 MB 的 String,光是读取/删除就要毫秒甚至秒级。用 redis-cli --bigkeys 定期扫描找出它们,拆分存储。
  2. 用 SCAN 代替 KEYS。第 16、34 章都强调过,KEYS * 会全库遍历并阻塞事件循环;SCAN 游标分批、不阻塞。
  3. 控制 Lua 脚本与事务体量。脚本里的循环、大范围读写都会变成一次长阻塞,脚本要尽量短小(见第 23 章)。
  4. 限制单次返回大小。比如 LRANGE mylist 0 -1 对超长列表极危险,改成分页或 LIMIT。返回越小,输出缓冲区压力越小,也越不容易触发 maxmemory-clients 驱逐。

四、监控与诊断

调优离不开观测。几个最常用:

127.0.0.1:6379> INFO clients
# Clients
connected_clients:1
maxclients:10000
blocking_clients:0

connected_clients 持续逼近 maxclients,说明连接池可能泄漏或上限该调;blocking_clients 高说明大量连接卡在阻塞命令上。

慢查询日志帮你定位「是谁慢」:

127.0.0.1:6379> SLOWLOG GET 10
1) 1) (integer) 14
   2) (integer) 1710000000
   3) (integer) 12345
   4) 1) "KEYS"
      2) "*"
   ...

第三条数字就是耗时(微秒),上面这条 KEYS * 花了 12 毫秒——典型的反面教材。

定期扫描大 key:

$ redis-cli --bigkeys

此外,INFO stats 里的 instantaneous_ops_per_sec(每秒命令数)和 INFO memoryused_memory 是观察整体负载与内存走向的基础指标(详见第 38 章性能监控)。

提示:码上学建议把「连接池大小」「timeout」「maxmemory-clients」「慢查询阈值 slowlog-log-slower-than」作为上线前必查的四项,缺一项都可能在上线后半夜报警。

小结

  • 连接池通过复用连接消除频繁建连开销,并约束连接总数;Jedis 用 JedisPool,redis-py 内建池且可显式设 max_connections
  • 服务端 timeout 清理空闲连接、tcp-keepalive 探活死连接、maxclients 限制并发连接数。
  • maxmemory-clients 限制客户端输出缓冲区总内存,是防止慢消费者拖垮实例的关键护栏。
  • 性能红线只有一条原则:别让单条命令阻塞事件循环——远离大 key、KEYS、重 Lua 和超大返回。
  • INFO clientsSLOWLOG GETredis-cli --bigkeys 持续观测,问题才藏不住。

常见误区

  • 以为「不关连接」最省事,于是不用连接池、每次新建。结果是握手开销巨大,且容易把 maxclients 打满。
  • timeout 设得过小(如 5 秒)又用长连接池,导致正常空闲连接被服务端频繁踢掉,连接池反复重建。长连接池通常配合 timeout 0
  • 只盯着 CPU 和内存,忽视客户端输出缓冲区。一个慢消费者就能靠膨胀的缓冲区吃光内存,必须靠 maxmemory-clients 兜底。

提示:官方 redis.io 下载页另将 8.8 标注为 “Latest stable”,而 GitHub 上的最新发布 tag 为 8.10.0;二者同属 8.x,配置与协议层面差异对本教程影响极小,本教程统一以 Redis 8.x(最新稳定版)表述。