首页 / Redis 入门教程 / 客户端连接与 RESP 协议

Redis 入门教程

客户端连接与 RESP 协议

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

客户端RESP连接模型maxclientsCLIENT 命令

32. 客户端连接与 RESP 协议

Redis 之所以快,除了数据放在内存里,另一个常被忽略的原因是它的网络层设计:用单线程、非阻塞 I/O 多路复用来处理成千上万个客户端连接。本章我们先看清一个客户端从「连上」到「通信」的全过程,再深入它说话用的语言——RESP 协议,最后落到生产中最常被问到的「最多能连多少」「怎么排查连接」这类问题上。

本节目标

  • 理解 Redis 接收连接时做了哪几件事,以及「单线程为何能扛高并发」的底层逻辑。
  • 看懂 RESP2 的五种基础类型在线路上长什么样,并能识别 RESP3 相比 RESP2 多了什么。
  • 掌握 maxclients 的含义、默认值与修改方式,知道连接数上限由操作系统文件描述符决定。
  • 熟练使用 CLIENT LISTCLIENT SETNAMECLIENT KILL 等命令排查与管控连接。
  • 能用 redis-cli-2 / -3 选项在 RESP2 与 RESP3 协议之间切换并观察差异。

连接是怎么建立的

当 Redis 在配置好的 TCP 端口(或启用的 Unix socket)上接受一个新的客户端连接时,会依次做三件事:

  1. 把客户端 socket 设为非阻塞模式。因为 Redis 的网络事件处理采用的是非阻塞多路复用模型,所有连接共享同一个事件循环,不会为某个连接单独开线程阻塞等待。
  2. 设置 TCP_NODELAY 选项,禁用 Nagle 算法。这样可以避免小数据包被合并导致的发送延迟,保证命令尽快到达对端。
  3. 创建一个可读文件事件,一旦 socket 上有新数据可读,事件循环就会把客户端发来的查询收集起来交给命令执行器。

正因为「一个线程 + 事件循环 + 非阻塞 socket」这套组合,Redis 才能用单线程顺序处理海量连接上的命令,而不需要为每个连接分配一个操作系统线程。代价是:单条命令不能执行太久(否则会阻塞整个事件循环),这也是为什么官方反复强调「避免大 key、避免慢查询」。

我们可以通过 INFO clients 实时观察当前连接情况:

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

其中 connected_clients 是当前已建立的客户端数量,maxclients 是允许的最大值(见下文),blocking_clients 是正在执行阻塞式命令(如 BLPOPXREADBLOCK)的客户端数。

最大连接数 maxclients

redis.conf 里有一个 maxclients 配置项,决定最多允许多少个客户端同时连上来。它的默认值是 10000,但这个值实际上受操作系统允许的最大文件描述符数约束——每个客户端连接都对应一个文件描述符。如果系统的 ulimit 比 maxclients 小,Redis 会按实际可用文件描述符来限制连接数(Redis 启动时会打印相关警告)。

查看当前生效值:

127.0.0.1:6379> CONFIG GET maxclients
1) "maxclients"
2) "10000"

可以在启动时通过命令行参数调整,也可以在配置文件里改:

redis-server --maxclients 100000

提示:把 maxclients 调得很大之前,先确认操作系统的 nofile 上限足够(例如 ulimit -n)。Redis 8.x(最新稳定版)对文件描述符的使用依旧遵循这一规则,光改配置不改系统限制是无效的。

客户端管控命令

Redis 提供了一组 CLIENT 子命令,用来观察、命名和切断连接,运维排查时非常有用。

CLIENT LIST —— 看谁连着

127.0.0.1:6379> CLIENT LIST
id=3 addr=127.0.0.1:51820 fd=8 name=worker-a age=42 idle=3 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 tot-net-in=1042 tot-net-out=8832 events=r cmd=ping user=default

每一行是一个客户端,字段含义关键的有:addr(客户端地址)、name(连接名)、age(已连接秒数)、idle(空闲秒数)、db(当前选中的库)、cmd(最近一条命令)、omem(输出缓冲区内存,排查「客户端输出缓冲区暴涨」时重点看)。

CLIENT SETNAME / CLIENT GETNAME —— 给连接起名

给当前连接起一个可读的名字,便于在 CLIENT LIST 里区分业务:

127.0.0.1:6379> CLIENT SETNAME order-service
OK
127.0.0.1:6379> CLIENT GETNAME
"order-service"

CLIENT KILL —— 掐掉某个连接

当某个客户端异常占用或需要强制下线时,可以按地址、名称或 ID 杀掉它:

127.0.0.1:6379> CLIENT KILL addr 127.0.0.1:51820
OK

CLIENT PAUSE —— 暂停所有客户端

以毫秒为单位挂起所有客户端,常用于需要在不影响数据一致性的前提下做某些运维操作(如切换、迁移)的场景:

127.0.0.1:6379> CLIENT PAUSE 5000
OK

5000 毫秒内所有客户端请求都会被服务端缓存排队,超时后恢复。

RESP 协议:客户端与服务端说的语言

RESP(REdis Serialization Protocol)是 Redis 客户端与服务端之间通信的协议。它设计目标很简单:对人类可读、解析简单、二进制安全。理解它,有助于你看懂 redis-cli 之外的「底层发生了什么」,也为后续理解客户端库、管道、Pub/Sub 的推送机制打基础。

RESP2 的五种基础类型

RESP2 用「第一个字节」标识类型,每条消息以 \r\n 结尾:

类型首字节示例(线路上的字节)含义
简单字符串++OK\r\n无二进制内容的成功响应
错误--ERR wrongtype\r\n错误信息
整数::1000\r\n整数结果(如 INCR 返回值)
批量字符串$$5\r\nhello\r\n带长度前缀的二进制安全字符串
数组**2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n有序的元素集合

看一个真实的交互。客户端向服务端发送 GET foo 时,线路上实际发的是:

*2\r\n$3\r\nGET\r\n$3\r\nfoo\r\n

也就是「一个包含两个批量字符串的数组,第一个是 GET,第二个是 foo」。如果 foo 的值是 bar,服务端回复:

$3\r\nbar\r\n

如果是整数命令,比如 INCR counter,回复以 : 开头:

:42\r\n

错误则以 - 开头,后面跟错误类型与消息:

-WRONGTYPE Operation against a key holding the wrong kind of value\r\n

$ 后面的数字表示接下来字符串的字节长度;特殊值 $-1\r\n 表示「空」(nil),比如对一个不存在的 key 执行 GET 时返回的就是这个。

RESP3:更丰富的类型与推送能力

RESP2 只有上面五种类型,很多返回结构(比如哈希、集合)只能用「数组」来近似表示,客户端拿到后还得靠约定去「猜」哪段是字段、哪段是值。从 Redis 6.0 起引入 RESP3,在保持向后兼容的前提下新增了多种类型,到了 Redis 8.x(最新稳定版)已是默认推荐协议。新增的关键类型包括:

  • %(Map,映射):直接表示键值对集合,例如 HGETALL 在 RESP3 下返回的就是带类型的 Map,而不再是扁平数组。
  • ~(Set,集合):表示无序去重集合。
  • >(Push,推送):用于服务端主动推数据,最典型的是 Pub/Sub 的消息——在 RESP3 下订阅消息以 Push 类型送达,不再占用普通回复通道。
  • ,(Double,双精度浮点)、#(Boolean 布尔,#t / #f)、_(Null 空)、!(Blob error 二进制错误)、=(Verbatim string 带格式字符串)、((Big number 大整数)。

你可以用 redis-cli 亲身体验两种协议的区别。默认 redis-cli 使用 RESP3(新版本),如果想强制退回 RESP2,用 -2;强制使用 RESP3 用 -3

$ redis-cli -2
127.0.0.1:6379> HGETALL user:1
1) "name"
2) "tom"
3) "age"
4) "20"
$ redis-cli -3
127.0.0.1:6379> HGETALL user:1
1# "name" => "tom"
2# "age" => "20"

注意在 RESP3 模式下,HGETALL 的输出预览把「字段 ⇒ 值」的映射关系展示得更清晰,这背后就是 Map 类型在起作用。绝大多数现代客户端库(详见第 33、34 章)已默认协商 RESP3,对应用层透明,你通常无需关心,但理解它有助于排查协议级别的兼容问题。

提示:RESP2 仍是众多老版本客户端的默认协议,Redis 8.x(最新稳定版)服务端两种都支持。若你的某个旧客户端连接后行为异常(例如订阅收不到消息),优先确认它协商的是哪种协议版本。

小结

  • Redis 用单线程事件循环 + 非阻塞多路复用处理所有连接,每个连接对应一个文件描述符。
  • maxclients 默认 10000,但实际上限受操作系统文件描述符限制;调大前先调系统 ulimit。
  • CLIENT LIST/SETNAME/GETNAME/KILL/PAUSE 是排查连接问题、区分业务、紧急下线客户端的核心命令。
  • RESP 是 Redis 的通信协议:RESP2 有五种基础类型(+ - : $ *),RESP3 增加了 Map、Set、Push、Double、Boolean 等,使返回结构更精确、Pub/Sub 推送更高效。
  • redis-cli-2 / -3 可在两种协议间切换,方便对比观察。

常见误区

  • 误以为「单线程」=「只能处理一个连接」。实际上单线程指的是命令执行是串行的,但连接接入、网络读写由事件循环并发处理,所以能同时维持上万连接,瓶颈在单条命令的耗时而非连接数。
  • 误以为 maxclients 配多大就能连多少。若系统 nofile 上限更小,Redis 实际能接受的连接数会被系统限制截断。
  • 看到 HGETALL 在两种协议下输出格式不同就以为是 bug。这其实是 RESP2(扁平数组)与 RESP3(Map 预览)的正常差异。

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