1. WATCH:监视键变化的乐观锁机制
工作原理与基本用法
在后端开发必读的 Redis 事务实战中,WATCH 是实现乐观锁的关键步骤之一,它帮助应用在提交事务前对指定键进行监视,确保并发环境下的数据一致性。通过对一个或多个键设定监视,当这些键在事务执行前被其他客户端修改,Redis 将中止事务的执行,从而避免脏读和冲突。
监视的键通常是业务关键信息,如库存数量、账户余额等,选择合适的监视键对并发控制至关重要。WATCH 的核心思想是先读取数据、再决定是否进入 MULTI/EXEC 的提交阶段,而不是在事务中直接锁住资源。
在具体实现中,WATCH 配合后续的 MULTI、EXEC、DISCARD四步来完成完整的事务控制序列,确保只有在数据未被外部修改的前提下才执行事务中的命令。下面的示例展示了 WATCH 的基本用法与流程要点。
WATCH inventory:book:1
GET inventory:book:1
MULTI
DECR inventory:book:1
INCR sold:book:1
EXEC
2. MULTI:开启事务,队列化指令
开启方式与队列化执行
一旦进入事务阶段,MULTI 将把随后的命令以队列方式暂存起来,而不是立即执行。这样的设计确保了在执行阶段内,所有指令要么同时执行,要么全部不执行,从而实现原子性。对于后端服务而言,这种行为非常适合处理诸如库存扣减与销售记录的一致性操作。
在使用 MULTI 时,后续的命令会组成一个原子执行单元。若在执行前出现异常,开发者可以通过 DISCARD 将队列中的命令清空,从而避免对数据造成不确定的副作用。
需要注意的是,MULTI 之后的命令不会立即执行,而是进入队列等待 EXEC 的提交时刻。若前置监视键未被修改,EXEC 将把队列中的命令作为一个原子操作执行;如果被修改,执行将返回空,表示需要重试。
WATCH inventory:book:1
GET inventory:book:1
MULTI
DECR inventory:book:1
INCR sold:book:1
EXEC
3. EXEC:提交事务,原子执行
返回结果与处理逻辑
执行阶段的核心是 EXEC,它尝试将队列中的所有命令作为一个原子操作一次性提交。如果在 WATCH 的监视键被其他客户端修改之前,数据保持一致,EXEC 将返回命令执行的结果数组,表示每条命令的执行情况。
如果在提交前监视的键发生了变更,EXEC 将返回一个空回复(nil),这意味着事务未能成功执行。在这种情况下,后端需要实现重试策略,重新读取数据、重新设置 WATCH,然后再次进入 MULTI/EXEC 的流程。
在实际场景中,EXEC 的返回结果通常是一个数组,例如包含库存扣减和销售记录更新的各命令的返回值。正确处理“重试、幂等性、以及错误分支”是稳定的后端设计要点之一。
WATCH inventory:book:1
GET inventory:book:1
MULTI
DECR inventory:book:1
INCR sold:book:1
EXEC
4. DISCARD:放弃事务,清空队列
适用场景与注意事项
在某些情况下,交易无法继续进行,例如监视键在提交前被其他客户端修改,或者业务逻辑在读取数据后判断不再需要提交。此时可以使用 DISCARD 明确放弃已排队的命令,释放 WATCH 的键并清空事务队列,确保系统不会对数据执行未期望的修改。
DISCARD 的使用时机通常包括“发现条件不满足后取消”、“需要重新评估请求”、“错误分支的回滚”等场景。它与 EXEC 是互斥关系:在进入 MULTI 之后,若不希望提交,应优先调用 DISCARD,而不是直接让事务继续传递到 EXEC。
在设计轨迹时,务必将 DISCARD 与错误处理逻辑结合起来,例如在客户端实现重试策略时,当检测到监视键被外部修改,应采取 DISCARD 加上重新读取数据的方式来重新进入新的事务流程。

WATCH key1
DISCARD


