Redis联合锁的那些事儿,怎么实现和用起来有啥讲究
- 问答
- 2026-01-02 10:37:07
- 2
Redis联合锁,说白了,就是同时给多个资源上锁,并且要求要么全部锁成功,要么一个都不锁,这事儿听起来简单,但做起来比给单个资源上锁要复杂得多,里面有不少讲究。
为啥需要联合锁?
想象一个常见的电商场景:用户A的账户里有100块,他同时下了两个订单,订单1要买一个80块的键盘,订单2要买一个50块的鼠标,如果这两个订单的支付操作同时进行,而系统没有做任何控制,可能会发生这样的事:
- 检查订单1:用户余额100 > 80,允许支付,准备扣款。
- 同时,检查订单2:用户余额100 > 50,允许支付,准备扣款。
- 订单1扣款80,余额变为20。
- 订单2扣款50,余额变为 -30,这就出现了透支,显然是不对的。
问题的根源在于,两个操作都只锁住了“订单”这个资源(比如用订单ID作为锁),但没有锁住它们共享的“用户账户”这个更关键的资源,联合锁就是为了解决这个问题而生的,它要求在执行敏感操作前,必须同时锁住“用户A的账户”和“订单1”(或“订单2”)。
怎么实现?核心是“原子性”

给单个资源加锁,我们通常用Redis的SET key value NX PX timeout命令(NX表示只有key不存在时才设置,PX设置过期时间),但联合锁需要锁住多个key,比如lock:user:123和lock:order:456。
最直接的错误做法是依次执行两个SET命令,问题在于,如果刚锁住lock:user:123,在准备锁lock:order:456的时候,Redis或者网络出问题了,导致第二个锁没加上,这下可好,lock:user:123这个锁就永远挂在那里了(直到过期),其他需要操作这个用户的请求全部被阻塞,这就是典型的死锁风险。
实现联合锁的核心讲究在于原子性:必须保证多个锁的获取操作是一个不可分割的整体,Redis本身没有直接提供给多个key同时加NX锁的命令,但我们可以通过其他方式模拟。

一种常见的方法是使用Lua脚本,因为Lua脚本在Redis中是原子执行的,在执行过程中不会被其他命令打断,我们可以写一个这样的脚本:
-- KEYS[1] = lock:user:123, KEYS[2] = lock:order:456
-- ARGV[1] = 唯一值1, ARGV[2] = 唯一值2, ARGV[3] = 过期时间(毫秒)
if redis.call('setnx', KEYS[1], ARGV[1]) == 1 and redis.call('setnx', KEYS[2], ARGV[2]) == 1 then
redis.call('pexpire', KEYS[1], ARGV[3])
redis.call('pexpire', KEYS[2], ARGV[3])
return 1 -- 返回1表示加锁成功
else
return 0 -- 返回0表示加锁失败(至少有一个锁已被占用)
end
这个脚本的好处是,要么两个锁都成功加上并设置了过期时间,要么就失败,避免了那种“加了一半”的尴尬局面,这里提到的“唯一值”(比如UUID)也很讲究,这是为了确保解锁时只能由加锁者来解,避免误删了其他客户端的锁。
用起来有啥讲究?
- 锁的粒度要合适:锁太多资源,会影响系统并发度;锁得太少,又起不到保护作用,需要权衡,比如上例中,锁住“用户账户”是必须的,但是否要锁住“商品库存”呢?这要看业务逻辑。
- 设置合理的过期时间:这是所有Redis锁的通用讲究,但联合锁尤其重要,过期时间太短,业务没执行完锁就释放了,会导致数据混乱;过期时间太长,万一客户端崩溃,资源被锁的时间会很长,通常需要根据业务操作的平均耗时来设定,并留有余量。
- 必须有解锁逻辑,且要同样原子:加锁是原子的,解锁也必须是原子的,不能先解了第一个锁,在解第二个锁之前发生异常,导致第二个锁成了“僵尸锁”,同样,解锁的Lua脚本需要先检查锁的值是否还是自己设置的那个“唯一值”,然后再删除。
- 考虑重试机制:联合锁获取失败的概率比单锁高,因为只要其中任何一个资源被占用就会失败,客户端通常需要实现一个重试机制,比如间隔一段时间后重试,但重试次数要有上限,避免无限等待。
- 并非万能药,复杂场景需升级:这种基于SETNX的联合锁在一般场景下够用,但如果业务非常复杂,锁的依赖关系可能形成“循环等待”(A等B,B等C,C又等A),就有死锁风险,或者对可靠性要求极高,可以考虑使用更重的方案,比如基于Redis Redlock算法的实现(但它也有争议),或者直接使用ZooKeeper等专门的一致性协调服务。
Redis联合锁是处理并发环境下多资源竞争的有效手段,但其实现和使用的关键就在于“原子性”这三个字,通过Lua脚本确保操作的原子性,再配合合适的锁粒度、过期时间、解锁和重试机制,才能让它既安全又高效地服务于业务。
本文由颜泰平于2026-01-02发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://waw.haoid.cn/wenda/73028.html
