redis 6.0 默认使用 resp2,必须显式指定 protocol=3 才启用 resp3;连接后执行 hello 3 可验证是否成功切换,否则布尔、浮点、map 等新类型将无法正确解析。

Redis 6.0 默认启用 RESP2,protocol=3 必须显式声明才能启用 RESP3 —— 否则客户端根本收不到布尔、浮点、map 等新类型,只会退化为字符串或数组模拟,还可能触发 ResponseError 或静默丢弃。
如何确认你的 redis-py 正在用 RESP3
光看 Redis 服务器版本没用。redis-py 默认仍走 RESP2,除非你主动指定协议版本并确保服务端支持。
- 连接时必须传
protocol=3参数,例如:redis.Redis(host="localhost", port=6379, protocol=3) - 若使用
redis.asyncio.Redis,同样需加protocol=3,否则异步客户端默认仍是 RESP2 - 连接后可发
HELLO 3命令验证:r.execute_command("HELLO", 3),成功返回一个dict表示已切换;失败则抛ConnectionError或ResponseError - 注意:Redis 6.0+ 服务端必须开启
io-threads-do-reads yes(非必需但影响推送能力),且不能运行在protected-mode yes+ 无密码的局域网外环境,否则HELLO可能被拒绝
RESP3 类型在 Python 中的映射行为差异
RESP2 把一切往 str 或 list 上塞,RESP3 则按语义还原——但这个“还原”依赖解析器是否启用、以及你是否禁用了自动解码。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
-
True/False:RESP2 返回"1"或"0"字符串;RESP3 直接返回 Pythonbool,前提是没设decode_responses=True(该参数会强制把所有字符串解码,反而覆盖原生布尔) -
float:RESP2 返回字符串如"3.14159",需手动float();RESP3 原生返回float,但若启用了decode_responses=True,它会被转成字符串再解码,丢失精度 -
map(即哈希结构):RESP2 返回list如["k1","v1","k2","v2"];RESP3 返回dict,键值顺序与服务端一致(Python 3.7+ 保证插入序) -
null:RESP2 用None模拟空数组或空字符串,歧义大;RESP3 的None明确对应_类型,且r.get("missing_key")在 RESP3 下真返回None,而非空字符串
跨语言客户端兼容性陷阱
RESP3 不是“全语言开箱即用”,各客户端库对新类型的实现进度不一,尤其在错误处理和边缘类型上容易不一致。
- Go 客户端
github.com/go-redis/redis/v9默认启用 RESP3,但Set类型仍返回[]interface{}而非map[interface{}]struct{},需手动转换 - Node.js 的
ioredis需设置enableReadyCheck: false并手动发HELLO 3,否则连接阶段就卡在 RESP2 兼容握手 - Java 的
lettuce6.x 支持 RESP3,但Attribute和Push类型默认被忽略——除非你注册自定义CommandOutput - 最危险的是混合使用:PHP 客户端(如
predis)尚未完全支持 RESP3 的double和boolean,若 Python 写入True,PHP 读出来可能是"t"或解析失败,导致逻辑分支错乱
为什么客户端缓存(client-side caching)必须依赖 RESP3
RESP2 根本没有机制让服务端“主动推消息”。所谓客户端缓存,本质是服务端在 key 变更时,通过 PUSH 类型通知所有订阅了该 key 的客户端——而 PUSH 是 RESP3 独占类型,RESP2 解析器遇到以 > 开头的响应直接报错或跳过。
- 启用前必须先执行
CLIENT CACHING ON,且连接需有client-output-buffer-limit宽松配置,否则推送消息被截断 - key 订阅不是靠命令,而是靠首次
GET后服务端自动记住——这意味着你不能用 pipeline 批量读,否则服务端无法区分哪个 key 要监听 - 收到
PUSH消息时,redis-py 会调用handle_push_response,但默认不暴露给用户;你要自己继承Connection类重写该方法,或改用asyncio版本配合pubsub模式捕获 - 别指望“自动同步”:服务端只推变更事件,不推新值;客户端仍要自己
GET一次,只是这次能命中本地内存缓存
RESP3 的价值不在“多几个类型”,而在于它把协议从“请求-响应管道”升级为“带语义的双向信道”。但这也意味着,一旦某环节(服务端配置、客户端库版本、连接参数、甚至 pipeline 使用方式)没对齐,类型就会坍缩回 RESP2 的模糊表达,而且往往不报错,只悄悄出错。










