WATCH 乐观锁是什么
WATCH 是 Redis 的乐观锁:先监视一个或多个键,再用 MULTI 开启事务,执行 EXEC 时如果被监视的键被其他客户端改过,事务就不执行、EXEC 返回空结果。标准用法是循环重试,适合冲突概率不高的读改写场景。
基本信息
| 流程 | WATCH 监视 → 读值 → MULTI → 写命令 → EXEC |
|---|---|
| 冲突表现 | EXEC 返回空结果,事务未执行 |
| 解除监视 | UNWATCH,或 EXEC、DISCARD 之后自动清除 |
| 作用范围 | 监视状态绑定在当前连接上 |
| 适用分布 | 低冲突键;高冲突时改用 Lua 脚本或单命令原子操作 |
典型使用场景
- 库存扣减:先读库存判断是否充足,事务内扣减,冲突就重试
- 余额与流水一致:读改写期间防止其他写入插队
- 配置更新:只在当前值仍符合预期时才写入
- 缓存重建:未命中后写回,避免覆盖并发产生的新值
- 重试策略验证:统计冲突率,据此决定是否更换方案
- 集群注意:被监视的键与事务内的键必须落在同一分片
库存扣减示例
库存扣减示例
WATCH stock:1001 # 监视库存键
GET stock:1001 # 读当前值,判断业务条件是否满足
MULTI # 开启事务
DECR stock:1001 # 入队写入
EXEC # 键被改过则返回空,需要重新开始
UNWATCH # 本轮放弃时清除监视状态
# 客户端伪代码:循环 watch/read/multi/exec,EXEC 返回空就重试EXEC 返回读法
| 返回 | 含义 | 处理方式 |
|---|---|---|
| 结果数组 | 事务正常执行 | 按顺序处理各条命令的结果 |
| 空结果 | 被监视键已变化,事务放弃 | 回到开头重试 |
| 错误 | 入队或执行阶段出错 | 按错误类型修正后重试 |
| 连接断开 | 会话失效,监视状态丢失 | 重建连接并清理客户端状态 |
重试要设上限并加退避:在高冲突键上无上限重试,延迟和失败率会一起被放大。
WATCH 与 EXEC 之间要尽量短:中间夹入慢命令或远程调用会显著抬高冲突概率,这也是要把读改写设计得足够轻的原因。