欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > Redis学习专题(六)分布式锁

Redis学习专题(六)分布式锁

2025/5/26 6:32:30 来源:https://blog.csdn.net/2303_80933038/article/details/148201827  浏览:    关键词:Redis学习专题(六)分布式锁

目录

一、情景再现

二、分布式锁主流实现方案

三、实现Redis分布式锁

0、启动redis

1、指令:setnx key value

2、指令:del key

3、指令 :expire key seconds

4、指令:ttl key

5、指令:set key value nx ex seconds

四、代码实现分布式锁

五、优化分布式锁

1、死锁

解决办法:设置过期时间

2、误删锁

解决办法:设置UUID

3、删除缺乏原子性

解决办法:LUA脚本保证删除原子性

思考:

核心区别:原子性​​


一、情景再现

  1. 单体单机部署的系统被演化成分布式集群系统后
  2. 由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效
  3. 单纯的Java API并不能提供分布式锁的能力
  4. 为了解决这个问题就需要一种跨JVM互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题
  5. 示意图(说明:我们探讨的分布式锁是针对分布式项目/架构而言[...],和redis集群无关)

单体项目锁机制示意图:

分布式项目锁机制示意图:

二、分布式锁主流实现方案

1、基于数据库实现分布式锁

2、基于缓存Redis实现分布式锁

3、基于Zookeeper实现分布式锁

性能对比:

性能:redis最高

可靠性:Zookeeper最高

三、实现Redis分布式锁

0、启动redis

1、指令:setnx key value

setnx:上锁

key:锁的键

value:锁的值 

在这个key删除之前,不能执行相同key的上锁指令

2、指令:del key

删除key,也就是释放锁

3、指令 :expire key seconds

给锁key设置过期时间,目的防止死锁,一直不释放锁就会造成死锁

4、指令:ttl key

查看某个锁key的过期时间

5、指令:set key value nx ex seconds

设置锁的同时,指定该锁的过期时间,防止死锁。

这个指令的原子性的,防止setnx key value/setnx key seconds两条指令中间执行被打断。

到期自动删除

四、代码实现分布式锁

情景再现:

在单机redis下,用springboot+redis实现分布式锁的使用

示意图:

当index1、index2、index3都想去执行业务逻辑的时候,先要到redis中获得锁(执行setnx指令成功),然后执行业务逻辑,必须释放锁,不然后续的请求进不来造成死锁

 1、先初始化数据

2、执行以下代码:

    @GetMapping("/lock")public void lock() {//第一步:获取锁Boolean lock=redisTemplate.opsForValue().setIfAbsent("lock", "ok");if (lock){//这个key为num的要先设置默认值,否则会出现问题Object value = redisTemplate.opsForValue().get("num");//1.判断返回的value是否有值if(value == null || !StringUtils.hasText(value.toString())){return;}//2.有值就转成intint num = Integer.parseInt(value.toString());//3.将num+1再转回去redisTemplate.opsForValue().set("num", num + 1);//4.释放锁redisTemplate.delete("lock");}else {//获取锁失败,休眠100ms再此获取try {Thread.sleep(100);lock();//递归重新执行}catch (InterruptedException e){e.printStackTrace();}}}

3、保证Linux可以访问到springboot项目

4、用ab工具测试,指令:ab -n 1000 -c 100 http://ip:端口/api接口路径

请求1000次,每次按100请求 

num就变成了1000

五、优化分布式锁

1、死锁

在前面的代码中,我们没有设置分布式锁的过期时间,如果因为业务异常没有释放锁,可能会导致死锁

解决办法:设置过期时间

2、误删锁

用户A业务操作因为卡顿,超过了设置的锁过期时间,于是自动释放了,此时用户B就获取到锁了,但是可能用户A的卡顿结束就会去释放锁,本次释放的锁就是用户B的锁了。

也就是步骤3释放了1的锁,步骤5释放了用户B在步骤4设置的锁

属于是释放锁时未验证当前锁是否仍属于自己

解决办法:设置UUID

1、给锁设置唯一的UUID

2、释放锁的时候查看是不是设置的同一把锁

3、造成这个问题的本质是缺乏原子性

3、删除缺乏原子性

用户A和用户B操作步骤如下:

1)比较uuid发现一样

2)准备删掉,但是还没有删除时,在1步获取的锁到了过期期间自动释放

3)同时因为高并发情况下,B用户执行了第4步,瞬间冲进来了,获取/设置了锁lock

4)这时del lock就是B用户的锁lock,又出现了误删,也就是说A释放了B的锁 

解决办法:LUA脚本保证删除原子性

解答:

用指令得到lock,这个值是当前请求的锁的值,ARGV是uuid值,如果相等就删除

执行

这个Array.asList("lock")会传递给script的KEYS[1],uuid会传给ARGV[1]

思考:

都是判断uuid和key的值是不是一样的,为什么直接比较uuid不行

核心区别:原子性​
​方案​​执行方式​​是否原子​​是否可能误删​
​Lua 脚本​Redis 单线程执行(GET + DEL 一步完成)✅ 是❌ 不会误删
​普通 Java 代码​先 GET(Java端判断)→ 再 DEL(两步操作)❌ 非原子✅ 可能误删

 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词