编程 RESP3 不是 JSON:14 行类型表、HELLO 握手与客户端缓存

2026-10-06 00:03:53

RESP3 不是 JSON:14 行类型表、HELLO 握手与客户端缓存

RESP(REdis Serialization Protocol)是 Redis 客户端与服务器之间的线缆协议(wire protocol)。虽然是为 Redis 设计的,也可以用于其他 C/S 软件。它序列化整数、字符串、数组等类型,另外有专门的错误类型。

版本演进:

  • Redis 1.2 引入第一版
  • Redis 2.0 起 RESP2 成为标准通信方式
  • Redis 6.0 引入 RESP3,实验性 opt-in 支持,包含 HELLO 命令做握手与协议版本升级

在 Redis 7 及以前,RESP2 和 RESP3 客户端都能调用全部核心命令,但同一命令在不同协议版本下可能返回不同类型的回复。官方说法是未来版本可能改变默认协议版本,但 RESP2 不太可能完全废弃;新特性可能要求 RESP3。协议名称就叫 RESP3,不是 RESP v3。

协议描述

RESP 本质是支持多种数据类型的序列化协议,第一个字节决定类型。它是二进制协议,控制序列使用标准 ASCII 编码:'A' 是字节 65,CR(\r)=13、LF(\n)=10、SP=32。

客户端把命令作为 bulk strings 组成的数组发给服务器:数组第一个(有时第二个)bulk string 是命令名,其余元素是参数。服务器按命令实现和客户端协议版本回复一个 RESP 类型。

类型最低版本分类首字节
Simple stringsRESP2Simple+
Simple ErrorsRESP2Simple-
IntegersRESP2Simple:
Bulk stringsRESP2Aggregate$
ArraysRESP2Aggregate*
NullsRESP3Simple_
BooleansRESP3Simple#
DoublesRESP3Simple,
Big numbersRESP3Simple(
Bulk errorsRESP3Aggregate!
Verbatim stringsRESP3Aggregate=
MapsRESP3Aggregate%
SetsRESP3Aggregate~
PushesRESP3Aggregate>

RESP2 只有 5 种:+ - : $ *。RESP2 用 *-1 和 $-1 表示 null,用 $ 表示浮点数和布尔,用数组表示一切集合(List/Hash/Set/ZSet),客户端必须知道当前命令才能把数组还原成正确类型,这是 RESP2 的主要痛点。

RESP3 新增:Null(_)、Boolean(#)、Double(,)、Big numbers(()、Bulk errors(!)、Verbatim strings(=)、Map(%)、Set(~)、Attribute(|)、Push(>)、Stream。使用 \r\n 作为分隔符,每个编码后跟 CRLF。

几个类型的具体行为:

  • Verbatim string:以 = 开头,后面 3 个字节表示格式(如 txt),再接长度和内容,原样显示给用户。
  • Map:以 % 开头,后面是键值对数量。HGETALL 在 RESP3 下直接返回 Map,客户端不用再做奇偶下标配对。
  • Set:以 ~ 开头,SMEMBERS 返回 Set 类型。
  • Push:首字节是 >,形如 Array,但第一个元素总是字符串,表示推送类型(如 pubsub、monitor),用于 Pub/Sub、客户端缓存失效通知这类带外数据。客户端必须检查首元素来识别,处理完 push 后还需继续读取自己的 reply。
  • Streamed strings / streamed aggregate:以块编码方式发送长度未知的字符串;聚合类型(Array/Set/Map)也可不指定长度,用显式终止符结束传输。

客户端握手

新的 RESP 连接应以 HELLO 命令开始会话,做两件事:协商协议版本,返回服务器信息。

HELLO [protover [AUTH username password] [SETNAME clientname]]

第一个参数是期望的协议版本;默认连接从 RESP2 开始。若指定版本过高或不被支持,服务器回 -NOPROTO 错误。

HELLO 3

HELLO 成功回复是一个 Map。所有 RESP3 实现都必须给出的字段:

  • server:"redis" 或其他软件名
  • version:服务器版本
  • proto:服务器支持的最高 RESP 版本

Redis 实现里还会给出 id(连接标识)、mode(standalone/sentinel/cluster)、role(master/replica)、modules(已加载模块数组)。

为什么 RESP3 是为了客户端缓存

RESP3 的设计动因之一是客户端缓存(client-side caching):本地缓存子集降低延迟,但本地缓存与 Redis 数据之间需要失效通知。RESP2 缺少带外推送能力,很难做,于是 antirez 设计了下一代协议 RESP3。Push 类型让服务端能主动推送失效通知,客户端不需要轮询。

兼容性与落地

antirez 最初想 Redis 6 直接只支持 RESP3、不兼容 RESP2(文章 Why RESP3 will be the only protocol supported by Redis 6)。后来 Marc Gravell 提议用协商方式确定版本,才有了 HELLO 命令。最终 RESP3 向前兼容 RESP2,客户端升级后仍能访问旧服务。

Redis 请求统一使用 bulk strings 数组格式,整数/错误/简单字符串/null 只在响应里出现;只有客户端解析响应时才会遇到多种类型。

Kvrocks 案例:2023 社区 Roadmap 有用户希望支持 RESP3,权衡工作量与收益后没有推进。它已实现带外数据类型的读取,但对返回数据仍按 RESP2 处理。有实现者写了 RESP3 的 Go Reader/Value 编码解码(colobu),其中几点需要注意:编码时先读属性,属性是 Map 类型,数量是键值对数量;复杂类型递归编码;读取 Value 时要先检查是否有 Attribute 前缀。

参考地址

推荐文章

程序员茄子在线接单