用两个独立连接,一个WATCH某个key并排队事务,另一个中途抢先修改这个key,验证EXEC是怎么检测冲突并取消整个事务的。
用两个独立的连接模拟两个并发的客户端。
代码s1, s2 = 两个独立的连接 cmd(s1, "SET", "wk", "100") cmd(s1, "WATCH", "wk") cmd(s1, "MULTI") cmd(s1, "INCR", "wk") -- 排队,还没真的执行 cmd(s2, "SET", "wk", "999") -- 另一个客户端在这时机改了wk cmd(s1, "EXEC") -- s1尝试提交
这跟前两篇看到的"排队的命令EXEC时一定会执行"完全不同。
结果EXEC → *-1 (是nil,不是一个长度为1的数组) GET wk之后 → "999" (是s2改的值,s1的INCR没有生效)
关键:WATCH wk 让s1这个连接记住了"我提交事务的前提是wk这个key在我WATCH之后到EXEC之前,没有被任何人修改过"——s2抢先改了wk,Redis在EXEC执行前会检查这个前提,发现被破坏了,就取消整个事务,INCR wk这条早已排好队的命令一次都没有被执行,EXEC直接返回nil通知客户端"提交失败,你需要自己决定要不要重试"。
证明失败不是WATCH机制本身的问题,是检测到了冲突。
结果SET wk2 5 WATCH wk2 MULTI INCR wk2 -- 排队 (这次没有其它客户端插进来改wk2) EXEC → [:6] (是长度为1的数组,INCR执行成功,6=5+1) GET wk2 → "6"
每一步都是本文上面用两个独立连接跑出来的原始结果。