分布式锁“误删”的问题以及解决办法

解决分布式锁误删的办法

首先,什么是“误删”?

场景模拟:
1.  客户端A 成功获取了锁,并设定锁的自动过期时间为 10秒。
2.  由于某些原因(比如GC垃圾回收、网络延迟),A的业务逻辑执行了15秒才完成。
3.  在第10秒时,锁因为超时时间到了而自动释放了。
4.  此时,客户端B 成功获取了同一个锁。
5.  第15秒,A的任务终于执行完了,它就去执行释放锁的操作。
6.  结果就是:A把B刚持有的锁给删除了! 这就是可怕的误删

后果非常严重:B以为锁还在自己手里,继续执行业务逻辑,此时可能有客户端C也能拿到锁,导致数据错乱。

解决之道:给锁加上“指纹”

核心思路很简单:谁加的锁,谁才有资格解锁。 不能让任何人都能来释放锁。

具体实现方法是:在每个锁上贴一个唯一的“指纹”(一个唯一标识),释放锁的时候,要检查这个“指纹”是不是自己的。

实现步骤(以Redis为例):

1. 获取锁时,存入唯一标识(指纹)

当客户端获取锁时,不仅在Redis中设置一个键值对(`key=lock_name`),还要在值(value)中存入一个唯一标识。这个标识可以是:
   UUID + 线程ID
   或者任何能全局唯一代表当前客户端的字符串。

# 命令:SET lock_name unique_client_id NX PX 10000
# 含义:如果lock_name不存在(NX),则设置它的值为"unique_client_id",过期时间为10000毫秒(PX)

2. 释放锁时,先检查再删除(原子操作)

这是最关键的一步!释放锁时,不能直接`DEL lock_name`。必须要做两件事:
1.  获取当前锁的值,看看是不是自己当初设置的`unique_client_id`。
2.  如果是,才执行删除操作。

但注意! 这两步操作必须是原子的,不能分开。否则在检查和删除的间隙,锁可能因过期而被其他客户端获取, again造成误删。

如何实现原子操作?

通常使用 Lua 脚本。因为Lua脚本在Redis中是原子执行的。

// 释放锁的Lua脚本
if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
else
    return 0
end


   `KEYS[1]` 是锁的名字,就是上面的 `lock_name`。
   `ARGV[1]` 是你自己客户端的唯一标识,就是上面的 `unique_client_id`。

脚本逻辑:
“如果锁现在的值还等于我的标识,说明锁还是我持有的,那我就删除它;如果不是,说明它已经不属于我了(可能已经过期并被别人获取),那我就返回0,什么都不做。”

总结一下解决方案


1. 加锁 `SET lock_name 1 NX PX 10000` | `SET lock_name <unique_value> NX PX 10000` | 在锁中存入唯一客户端标识(指纹) |
2. 解锁 `DEL lock_name` | 执行一段 Lua 脚本,先比对`<unique_value>`再删除 | 原子地验证“指纹”,确保只能删自己的锁 |

额外的保障:设置合理的过期时间

误删的根本原因是业务执行时间超过了锁的过期时间。因此,除了上述方法,你还应该:

   设置一个合理的、安全的过期时间: 充分评估你的业务逻辑最长可能执行多久,并在此基础上增加一些缓冲时间。例如,业务最多执行5秒,那锁的过期时间可以设置为10-15秒。
   使用看门狗(Watch Dog)机制(高级): 一些成熟的分布式锁框架(如Redisson)提供了“看门狗”机制。它会在你获取锁成功后,以一个更短的时间间隔(比如过期时间的1/3)去定期检查业务是否还在执行。如果还在执行,就自动延长锁的过期时间,从而从根本上避免锁因业务未执行完而提前过期。

最终答案

要解决分布式锁误删问题,最核心、最必要的做法是:

在释放锁时,通过原子操作(如Lua脚本)验证锁的Value值是否与当前客户端的唯一标识匹配,匹配成功才能删除。

这样就确保了“谁加的锁,谁才能释放”,彻底避免了误删他人锁的风险。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值