Redis如何实现乐观锁的
ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花
一、核心前提:Redis乐观锁的底层逻辑
乐观锁的核心是不加锁、无阻塞,仅在最终提交操作时校验数据是否被并发修改:若数据未变则执行更新,若已被篡改则放弃本次操作并重试,避免线程阻塞。
Redis没有内置的乐观锁API,而是通过WATCH命令 + 事务(MULTI/EXEC)实现CAS(比较并交换)机制,这是Redis实现乐观锁的唯一标准方案。
核心原理:WATCH监控目标键的变更状态,事务提交前若键被修改,事务直接放弃执行,以此实现并发冲突校验。
二、核心命令详解
实现乐观锁需要配合4个关键命令,各司其职完成监控、事务、提交全流程:
- WATCH key [key ...]:开启键监控,标记指定键为“受监控状态”。一旦监控的键被其他客户端修改(增删改),当前客户端的事务会被打断。
- MULTI:开启事务队列,后续命令不会立即执行,而是存入事务队列缓存。
- EXEC:提交事务,批量执行队列中的命令。若监控键被修改,EXEC返回nil,事务不执行;若未修改,正常执行所有命令。
- UNWATCH:取消所有键的监控,常用于事务执行失败后重置状态。
Redis事务不支持回滚!EXEC执行前若监控键被篡改,事务直接丢弃,而非回滚,这是实现乐观锁的关键特性。
三、分步实现流程(以库存扣减为例)
以经典的商品库存并发扣减场景,拆解Redis乐观锁的完整执行步骤:
步骤1:监控目标库存键
客户端执行WATCH命令,监控库存键(如stock:1001),此时Redis会记录该键的当前版本/状态。
步骤2:读取当前数据,执行业务校验
客户端获取库存键的当前值,判断是否满足操作条件(如库存是否充足)。这一步是非原子的,允许并发读取。
步骤3:开启事务,编写修改命令
执行MULTI开启事务,将库存扣减命令(如DECRBY)加入事务队列,此时命令仍未执行。
步骤4:提交事务,触发CAS校验
执行EXEC提交事务,Redis会原子性校验:监控的stock:1001是否被其他客户端修改。
- 校验通过:执行队列中的扣减命令,返回执行结果。
- 校验失败:EXEC返回nil,事务丢弃,客户端需重试整个流程。
步骤5:失败重试/取消监控
若事务执行失败,先执行UNWATCH取消监控(避免影响后续操作),再重新发起WATCH+事务流程。
四、实战命令行演示
场景:商品1001初始库存100,两个客户端并发扣减库存
客户端A(正常执行)
# 1. 监控库存键 WATCH stock:1001 # 2. 读取当前库存(返回100) GET stock:1001 # 3. 开启事务,加入扣减命令 MULTI # 扣减1件库存 DECRBY stock:1001 1 # 4. 提交事务 EXEC # 执行结果:返回99(库存扣减成功)
客户端B(并发冲突,执行失败)
# 1. 监控库存键
WATCH stock:1001
# 2. 读取当前库存(返回100,此时客户端A尚未提交)
GET stock:1001
# 3. 开启事务,加入扣减命令
MULTI
DECRBY stock:1001 1
# 4. 提交事务前,客户端A已修改库存,触发冲突
EXEC
# 执行结果:返回nil(事务放弃,扣减失败)
# 5. 取消监控,准备重试
UNWATCH
五、关键注意事项
- 监控粒度:WATCH可监控多个键,仅当所有监控键均未修改时,事务才会执行;任意键修改,事务直接失效。
- 监控失效场景:客户端断开连接、执行UNWATCH、执行EXEC/DISCARD(事务提交/丢弃),都会自动取消监控。
- 非值变更也会触发失效:DEL、RENAME、EXPIRE等修改键结构/状态的命令,也会触发WATCH监控失效。
- 必须加重试机制:并发冲突时事务会失败,必须设置重试次数,避免单次操作直接报错。
- 不适合高并发冲突场景:若并发冲突极高,重试次数会激增,性能下降,此时建议改用分布式锁(如Redlock)。
六、适用场景
Redis乐观锁适合读多写少、并发冲突概率低的场景,比如:
- 商品库存扣减、优惠券核销
- 用户积分、余额变更
- 配置信息、计数更新
ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花
本专栏聚焦 Redis 分布式锁从原理到生产落地全流程,拆解SET 原子加锁、Lua 解锁、锁续期、Redlock等核心技术,直击锁超时、误释放、主从一致性等高频痛点。结合 Redisson 实战与微服务场景案例,输出可直接复用的代码方案与避坑指南,助力后端工程师攻克分布式并发难题,构建高可靠锁服务。