冲突后不能只重发 EXEC
A 的读取依据已经过时,上一轮排队的命令也被 Redis 丢弃。下一次尝试必须从 WATCH 开始,再次 GET 最新库存,然后重新计算并入队。
REDIS 并发实验 · 乐观锁
两个请求同时领取最后几张优惠券。连接 A 已经读到库存,但连接 B 抢先修改了同一个 key——看看 A 的事务为什么会整个放弃,以及一次正确重试究竟要重做哪些步骤。
stock:coupon:42
WATCH 状态属于执行它的那条 Redis 连接。A 从 WATCH 到 EXEC 必须留在同一条连接上;如果连接池中途换连接,新的连接既没有监视状态,也不能替 A 完成这次校验。
WATCH stock:coupon:42GET stock:coupon:42MULTI
DECR stock:coupon:42连接 B 可能写入同一 key成功返回结果;冲突返回空等待开始。可以自动运行,也可以单步推进到冲突窗口后手动让 B 插队。
| 尝试 | A 读取库存 | 监视版本 | EXEC | 退避 |
|---|---|---|---|---|
| 尚无记录 | ||||
A 的读取依据已经过时,上一轮排队的命令也被 Redis 丢弃。下一次尝试必须从 WATCH 开始,再次 GET 最新库存,然后重新计算并入队。
热点 key 持续被写时,某个请求可能反复冲突。退避能减少同时抢跑,最大重试次数则避免请求无限占用线程;达到上限后应返回“稍后再试”,并记录饥饿指标。