Redis分布式锁到底是怎么搞的,原理和用法其实没那么复杂
- 问答
- 2025-12-31 05:55:15
- 3
关于Redis分布式锁,很多人一听就觉得是那种特别高深、只有大厂专家才能搞明白的东西,其实不然,它的核心思想非常简单,就像我们日常生活中用的一把锁,想象一下,一个公共卫生间只有一个坑位,门上有一把钥匙,谁想上厕所,就得先拿到这把钥匙,用完之后再把钥匙放回去,后面的人才能用,分布式锁要解决的就是在多台机器(多个进程)并发的环境下,如何实现类似“一把钥匙”的效果。
核心原理:用一条Redis命令占坑
Redis是单线程处理命令的,这个特性是它能做分布式锁的基石,这意味着,无论多少客户端同时发来请求,Redis都会一个一个地执行,绝对不会出现并发问题。
那我们怎么在Redis里创造这把“钥匙”呢?最直接的想法就是,在Redis里设置一个唯一的键值对(Key-Value),这个Key就是锁的名字,order_lock_123”,表示对订单123的操作锁,Value呢,最好是一个唯一标识,比如UUID,用来证明这把锁是“我”加的,防止被其他人误删。
设置这个键值对的操作,就是我们实现锁的关键,我们不能用普通的SET命令,因为它可能覆盖掉别人已经设置的锁,我们需要一个能“原子性”完成“如果不存在则设置”的命令,这个命令就是SET key value NX PX 30000(来源:Redis官方命令文档)。
NX:意思是“Only set the key if it does not already exist”,只有当这个key不存在时,才设置它,这保证了只有第一个来的客户端能设置成功,也就是“抢到锁”。PX 30000:意思是给这个key设置一个30秒的过期时间。这是极其重要的一步! 它避免了如果客户端抢到锁后崩溃了,没能释放锁,导致锁永远无法被释放的“死锁”情况。
抢锁的过程就是:多个客户端同时向Redis发送SET lock_name my_uuid NX PX 30000命令,由于Redis是单线程,只有一个客户端的设置会成功,成功的那个客户端就获得了锁,可以继续执行后面的业务逻辑,比如扣减库存,失败的客户端就只能等待,通常是通过循环重试的方式再去抢锁。
释放锁:不能简单删除,要“验明正身”
业务逻辑执行完了,就需要释放锁,好让他人使用,释放锁就是把这个Key删掉,但这里有个大坑:你不能直接DEL lock_name。
想象一下这个场景:
- 客户端A抢到了锁,设置过期时间为30秒。
- 客户端A的业务逻辑执行了35秒(可能因为GC停顿或者网络延迟),此时Redis中的锁因为过期已经自动释放了。
- 客户端B趁这个空档抢到了锁。
- 这时,客户端A终于执行完了,它浑然不知锁已经不属于自己了,直接调用
DEL命令。 - 结果就是把客户端B刚加的锁给删掉了!客户端C就能趁机抢到锁,导致同时有两个客户端在执行关键业务,锁就失效了。
释放锁的时候,必须“验明正身”,在删除之前,要先判断一下当前锁的Value是不是自己当初设置的那个UUID,如果是,才能删;如果不是,就不能删,这个“判断+删除”的操作也必须是原子性的,不能分两步执行,否则在判断之后、删除之前,锁可能又过期被别人抢走了。
在早期,我们需要用Lua脚本来保证这个原子性(来源:Redis官方关于分布式锁的说明),Lua脚本在Redis中是单线程执行的,能确保一连串命令的原子性,脚本大概长这样:
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
这个脚本的意思是:用GET命令获取锁(KEYS[1])当前的值,如果它等于我传入的UUID(ARGV[1]),那我才有权删除它。
更简单的办法:使用现成的库
上面说的自己实现SET NX PX和Lua脚本的方式,是理解原理的根本,但在实际项目中,我们很少会自己去写这些底层代码,因为容易出错,现在有很多成熟的Redis客户端库已经封装好了分布式锁的实现。
比如在Java中,最常用的就是Redisson库(来源:Redisson开源项目文档),它提供的分布式锁使用起来非常简单:
RLock lock = redisson.getLock("myLock");
lock.lock();
try {
// ... 这里执行你的业务逻辑
} finally {
lock.unlock();
}
你不需要关心过期时间设置多少,也不需要写Lua脚本,Redisson帮你实现了上面所说的所有逻辑,并且还提供了更高级的功能,比如锁的自动续期(看门狗机制):如果业务逻辑执行时间较长,Redisson会在后台线程定期帮你延长锁的过期时间,防止业务没执行完锁就过期了,这样就大大降低了使用的门槛和出错的概率。
总结一下
Redis分布式锁的原理真的不复杂:
- 加锁:利用Redis单线程和
SET NX PX命令原子性地争抢一个Key,抢到就是获得锁。 - 设置过期时间:这是必须的,防止死锁。
- 释放锁:需要用Lua脚本原子性地验证Value再删除,防止误删别人的锁。
而用法就更简单了,对于大多数开发者来说,直接选择一个像Redisson这样经过验证的客户端库,调用它的加锁、解锁方法就可以了,底层复杂的细节库已经帮你处理妥当了,理解原理是为了在出现问题时能知道根因,而使用成熟的工具则是为了提升开发效率和保证代码的健壮性。

本文由符海莹于2025-12-31发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://waw.haoid.cn/wenda/71711.html
