bigywater.tech
Redis · 29

MULTI/EXEC 事务

用裸RESP协议手动发MULTI/命令/EXEC,逐条观察排队阶段和真正执行阶段分别发生了什么。

+QUEUED
排队期间每条命令统一的回复
EXEC才真执行
命令的业务逻辑全部延迟到EXEC那一刻
数组结果
EXEC返回逐条对应的结果数组

做的事:手写RESP协议,逐条观察MULTI做了什么

不用客户端库包装,直接拼RESP协议帧,看服务器每一步回了什么。

代码import socket s = socket.create_connection(("127.0.0.1", 19379)) def cmd(sock, *args): msg = f"*{len(args)}\r\n" for a in args: msg += f"${len(str(a))}\r\n{a}\r\n" sock.sendall(msg.encode()) return sock.recv(65536) print(cmd(s, "MULTI")) print(cmd(s, "SET", "counter", "20")) print(cmd(s, "INCR", "counter"))

验证:队列期间,每条命令立刻收到+QUEUED,不是真的执行结果

SET和INCR本该有自己的返回值(+OK和一个整数),收到的却完全不是这些。

结果MULTI → +OK SET counter 20 → +QUEUED (不是+OK!) INCR counter → +QUEUED (不是:21!)

验证了:进入MULTI状态后,服务器对后续每一条命令只做了两件事——检查这条命令本身是否存在、参数格式是否合法,然后把它原样放进一个队列,立刻回复+QUEUED,命令的业务逻辑(改数据、算结果)在这一步完全没有被执行。

验证:EXEC那一刻,队列里的命令按顺序一次性全部执行

这一批命令里故意混了一条会在运行时报错的LPUSH。

代码与结果MULTI SET counter 20 → QUEUED INCR counter → QUEUED LPUSH counter x → QUEUED (counter是字符串,这条命令注定会失败) SET another 1 → QUEUED EXEC → 返回: [+OK, :21, -WRONGTYPE Operation against a key holding the wrong kind of value, +OK]
关键点:EXEC返回的是一个数组,数组长度等于队列里命令的数量,每个元素是对应那条命令真的执行之后得到的结果——第1条SET成功(+OK),第2条INCR成功(:21),第3条LPUSH执行时才发现counter是字符串类型,返回的WRONGTYPE错误,第4条SET依然成功执行(+OK)。整个事务是"打包发送、逐条执行、结果打包返回",EXEC这一步之前的所有QUEUED回复,都只是告诉客户端"这条命令语法没问题,已经排进队了",跟命令真的执行完全是两件事。

分步演示:MULTI/EXEC的两阶段

每一步都是本文上面用裸RESP协议跑出来的原始结果。

排队时只检查语法,EXEC那一刻才执行
这一步新验证的阶段

    本文用到的工具

    • 本机 Redis 8.10.1
    • 裸socket + RESP协议:逐条观察MULTI/EXEC每一步的回复
    京ICP备2026067502号-1京公网安备11010502063487号