SpringBoot Redission实现分布式锁

网友投稿 889 2022-10-18

SpringBoot Redission实现分布式锁

SpringBoot Redission实现分布式锁

为了防止分布式系统中的多个进程之间相互干扰,我们需要一种分布式协调技术来对这些进程进行调度。 而这个分布式协调技术的核心就是来实现这个分布式锁。

分布式锁应该具备条件

互斥性 防止死锁 可重入 非阻塞 锁的力度

目前我所知道的有3种方式

通过 数据库实现排他锁 -- 性能比较差 不推荐通过 zookeeper 实现 -- 目前还不了解通过 redis(Redisson) 实现 -- 通过设置过期时间 来控制锁的获取及释放

Redisson 原理

Redisson分布式锁的实现是基于实现RLock接口

1、加锁机制

线程去获取锁,获取成功: 执行lua脚本,保存数据到redis数据库。 线程去获取锁,获取失败: 一直通过while循环尝试获取锁,获取成功后,执行lua脚本,保存数据到redis数据库。

2、watch dog自动延期机制(性能较差)

在一个分布式环境下,假如一个线程获得锁后,突然服务器宕机了,那么这个时候在一定时间后这个锁会自动释放,你也可以设置锁的有效时间(不设置默认30秒),这样的目的主要是防止死锁的发生。

3、使用lua脚本

通过封装在lua脚本中发送给redis,而且redis是单线程的,这样就保证这段复杂业务逻辑执行的原子性。

Redis分布式锁的缺点

Redis分布式锁会有个缺陷,就是在Redis哨兵模式下:

​​客户端1​​​ 对某个​​master节点​​写入了redisson锁,此时会异步复制给对应的 slave节点。但是这个过程中一旦发生 master节点宕机,主备切换,slave节点从变为了 master节点。

这时​​客户端2​​ 来尝试加锁的时候,在新的master节点上也能加锁,此时就会导致多个客户端对同一个分布式锁完成了加锁。

这时系统在业务语义上一定会出现问题,导致各种脏数据的产生。

​​缺陷​​在哨兵模式或者主从模式下,如果 master实例宕机的时候,可能导致多个客户端同时完成加锁。

自定义starter 封装Redission 结构如下   打成jar包

新建项目测试(引入jar包)

application.yml

yu: redisson: lock: server: address: 127.0.0.1:6379 password: redis type: standalone database: 0

/** * 不基于注解方式锁操作 */@RestController@Slf4jpublic class LockController { @Autowired RedissonLock redissonLock; /** * 模拟这个是商品库存 */ public static volatile Integer TOTAL = 10; @GetMapping("lock-decrease-stock") public String lockDecreaseStock() throws InterruptedException { redissonLock.lock("lock", 10L); if (TOTAL > 0) { TOTAL--; } Thread.sleep(50); log.info("===lock===减完库存后,当前库存===" + TOTAL); //如果该线程还持有该锁,那么释放该锁。如果该线程不持有该锁,说明该线程的锁已到过期时间,自动释放锁 if (redissonLock.isHeldByCurrentThread("lock")) { redissonLock.unlock("lock"); } return "================================="; } @GetMapping("trylock-decrease-stock") public String trylockDecreaseStock() throws InterruptedException { if (redissonLock.tryLock("trylock", 5L, 200L)) { if (TOTAL > 0) { TOTAL--; } Thread.sleep(50); redissonLock.unlock("trylock"); log.info("====tryLock===减完库存后,当前库存===" + TOTAL); } else { log.info("[ExecutorRedisson]获取锁失败"); } return "==================================="; } @GetMapping("annotatin-lock-decrease-stock") @DistributedLock(value="goods", leaseTime=5) public String lockDecreaseStock() throws InterruptedException { if (TOTAL > 0) { TOTAL--; } log.info("===注解模式=== 减完库存后,当前库存===" + TOTAL); return "================================="; } }

使用 jmeter 开启1000个线程 测试 是否会有超卖的问题

PS:大部分情况 都是使用 lock

1、tryLock锁是可能会等待的,因为当过了等待时间还没有获取锁,就会返回false,对于性能来说,这显然很致命!

2、注解锁只能用于方法上,颗粒度太大,满足不了方法内加锁。

在使用RedissonLock锁时,很容易报这类异常,比如如下操作

//设置锁1秒过去 redissonLock.lock("redisson", 1); //业务逻辑需要咨询2秒 redissonLock.release("redisson");

原因:

线程1 进来获得锁后,但它的业务逻辑需要执行2秒,在 线程1 执行1秒后,这个锁就自动过期了,那么这个时候线程2 进来了获得了锁。在线程1去解锁就会抛上面这个异常(因为解锁和当前锁已经不是同一线程了)

解决:

//如果为false就说明该线程的锁已经自动释放,无需解锁 if (redissonLock.isHeldByCurrentThread("lock")) { redissonLock.unlock("lock"); }

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:SciDAVis- 可视化数据分析程序
下一篇:mybatis中foreach嵌套if标签方式
相关文章

 发表评论

暂时没有评论,来抢沙发吧~