SpringBoot Redis分布式锁的正确实现方式(2)
SpringBoot
原因很简单:客户端加锁时,没有设置一个唯一标识。释放锁的逻辑并不会检查这把锁的归属,直接删除。
残血版分布式锁
小白老师:“不念,怎么解决释放别人的锁的情况呢?”
解决方法:客户端加锁时设置一个“唯一标识”,可以让 value 存储客户端的唯一标识,比如随机数、 UUID 等;释放锁时判断锁的唯一标识与客户端的标识是否匹配,匹配才能删除。
加锁
SET lockKey randomValue NX PX 3000
解锁
删除锁的时候判断唯一标识是否匹配伪代码如下。
if (jedis.get(lockKey).equals(randomValue)) {
jedis.del(lockKey);
}
加锁、解锁的伪代码如下所示。
try (Jedis jedis = pool.getResource()) {
//加锁成功
if(jedis.set(lockKey, randomValue, "NX", "PX", 3000) == 1){
do work //执行业务
}
} finally {
//判断是不是当前线程加的锁,是才释放
if (randomValue.equals(jedis.get(keylockKey {
jedis.del(lockKey); //释放锁
}
}
到这里,很多公司可能都是使用这个方式来实现分布式锁。
小白:“不念,还有问题。判断锁的唯一标识是否与当前客户端匹配和删除操作不是原子操作。”
聪明。这个方案还存在原子性问题,存在其他客户端把锁给释放的问题。
- 客户端 A 执行唯一标识匹配成功,还来不及执行
DEL释放锁操作,锁过期被释放。 - 客户端 B 获取锁成功,value 设置了自己的客户端唯一标识。
- 客户端 A 继续执行
DEL删除锁操作,相当于把客户端 B 的锁给删了。
青铜版分布式锁
虽然叫青铜版,这也是我们最常用的分布式锁方案之一了,这个版本没有太大的硬伤,并且比较简单。
小白老师:“不念,这如何是好,如何解决解锁不是原子操作的问题?分布式锁这么多门道,是我肤浅了。”
解决方案很简单,解锁的逻辑我们可以通过Lua脚本来实现判断和删除的过程。
KEYS[1]是 lockKey。ARGV[1]表示客户端的唯一标识 requestId。
返回 nil 表示锁不存在,已经被删除了。只有返回值是 1 才表示加锁成功。
// key 不存在,返回 null
if (redis.call('exists', KEYS[1]) == 0) then
return nil;
end;
// 获取 KEY[1] 中的 value 与 ARGV[1] 匹配,匹配则 del,返回 1。不匹配 return 0 解锁失败
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1]);
else
return 0;
end;
使用上面的脚本,每个锁都用一个随机值作为唯一标识,当删除锁的客户端的“唯一标识”与锁的 value 匹配的时候,才能执行删除操作。这个方案已经相对完美,我们用的最多的可能就是这个方案了。
相关阅读
-
b2b推广网站有哪些 垂直b2b电商平台推荐
跟大家聊一聊b2b推广网站有哪些和垂直b2b电商平台推荐方面的内容,下面小编为您详细解答 最近,很多小伙伴都反馈给我。我想知道哪些B2B网站在市场上做得很好。以下是IT袋网小编收集的全
-
前端开发工程师是做什么的 web前端的工作内容
IT袋网为大家说一说前端开发工程师是做什么的和web前端的工作内容方面的讲解,请看下面详细的介绍。 随着Internet的发展和多个终端的普及,前端开发工程师逐渐受到欢迎,但是前端开发工程
-
什么是Ceph 有什么特点?
如果想了解什么是CephIT技巧方面的经验,相关内容具体如下: 概述 Ceph是当前非常流行的开源分布式存储系统,具有高扩展性、高性能、高可靠性等优点,同时提供 块存储服务 (rbd)、 对象存储
-
[网络工程师]-网络规划与设计-通信规范分析
相对于大多数人[网络工程师]-网络规划与设计-通信规范分析方面的介绍,下面为详细的介绍。 在网络分析和设计过程中,通信规范分析处于第二个阶段,通过分析网络通信流量和通信模式,发


