有关 Redisson 作为实现分布式锁, 总的分 3 大模块来讲.
,Redisson 实现分布式锁原理
,Redisson 实现分布式锁的源码解析
,Redisson 实现分布式锁的项目代码 (可以用于实际项目中)
本文只介绍 Redisson 如何实现分布式锁的原理. 其它的会在接下来的博客讲, 最后有关 Redisson 实现分布式锁的项目代码的博客中会放上项目源码到 GitHub 上.
一, 高效分布式锁
当我们在设计分布式锁的时候, 我们应该考虑分布式锁至少要满足的一些条件, 同时考虑如何高效的设计分布式锁, 这里我认为以下几点是必须要考虑的.
1, 互斥
在分布式高并发的条件下, 我们最需要保证, 同一时刻只能有一个线程获得锁, 这是最基本的一点.
2, 防止死锁
在分布式高并发的条件下, 比如有个线程获得锁的同时, 还没有来得及去释放锁, 就因为系统故障或者其它原因使它无法执行释放锁的命令, 导致其它线程都无法获得锁, 造成死锁.
所以分布式非常有必要设置锁的有效时间, 确保系统出现故障后, 在一定时间内能够主动去释放锁, 避免造成死锁的情况.
3, 性能
对于访问量大的共享资源, 需要考虑减少锁等待的时间, 避免导致大量线程阻塞.
所以在锁的设计时, 需要考虑两点.
1, 锁的颗粒度要尽量小. 比如你要通过锁来减库存, 那这个锁的名称你可以设置成是商品的 ID, 而不是任取名称. 这样这个锁只对当前商品有效, 锁的颗粒度小.
2, 锁的范围尽量要小. 比如只要锁 2 行代码就可以解决问题的, 那就不要去锁 10 行代码了.
4, 重入
我们知道 ReentrantLock 是可重入锁, 那它的特点就是: 同一个线程可以重复拿到同一个资源的锁. 重入锁非常有利于资源的高效利用. 关于这点之后会做演示.
针对以上 Redisson 都能很好的满足, 下面就来分析下它.
二, Redisson 原理分析
为了更好的理解分布式锁的原理, 我这边自己画张图通过这张图来分析.
1, 加锁机制
线程去获取锁, 获取成功: 执行 lua 脚本, 保存数据到 Redis 数据库.
线程去获取锁, 获取失败: 一直通过 while 循环尝试获取锁, 获取成功后, 执行 lua 脚本, 保存数据到 Redis 数据库.
2,watch dog 自动延期机制
这个比较难理解, 找了些许资料感觉也并没有解释的很清楚. 这里我自己的理解就是:
在一个分布式环境下, 假如一个线程获得锁后, 突然服务器宕机了, 那么这个时候在一定时间后这个锁会自动释放, 你也可以设置锁的有效时间 (不设置默认 30 秒), 这样的目的主要是防止死锁的发生.
但在实际开发中会有下面一种情况:
- // 设置锁 1 秒过去
- redissonLock.lock("redisson", 1);
- /**
- * 业务逻辑需要咨询 2 秒
- */
- redissonLock.release("redisson");
- /**
- * 线程 1 进来获得锁后, 线程一切正常并没有宕机, 但它的业务逻辑需要执行 2 秒, 这就会有个问题, 在 线程 1 执行 1 秒后, 这个锁就自动过期了,
- * 那么这个时候 线程 2 进来了. 那么就存在 线程 1 和线程 2 同时在这段业务逻辑里执行代码, 这当然是不合理的.
- * 而且如果是这种情况, 那么在解锁时系统会抛异常, 因为解锁和加锁已经不是同一线程了, 具体后面代码演示.
- */
所以这个时候看门狗就出现了, 它的作用就是 线程 1 业务还没有执行完, 时间就过了, 线程 1 还想持有锁的话, 就会启动一个 watch dog 后台线程, 不断的延长锁 key 的生存时间.
注意 正常这个看门狗线程是不启动的, 还有就是这个看门狗启动后对整体性能也会有一定影响, 所以不建议开启看门狗.
3, 为啥要用 lua 脚本呢?
这个不用多说, 主要是如果你的业务逻辑复杂的话, 通过封装在 lua 脚本中发送给 Redis, 而且 Redis 是单线程的, 这样就保证这段复杂业务逻辑执行的原子性.
4, 可重入加锁机制
Redisson 可以实现可重入加锁机制的原因, 我觉得跟两点有关:
,Redis 存储锁的数据类型是 Hash 类型
,Hash 数据类型的 key 值包含了当前线程信息.
下面是 Redis 存储的数据
这里表面数据类型是 Hash 类型, Hash 类型相当于我们 java 的 <key,<key1,value>> 类型, 这里 key 是指'redisson'
它的有效期还有 9 秒, 我们再来看里们的 key1 值为 078e44a3-5f95-4e24-b6aa-80684655a15a:45 它的组成是:
guid + 当前线程的 ID. 后面的 value 是就和可重入加锁有关.
举图说明
上面这图的意思就是可重入锁的机制, 它最大的优点就是相同线程不需要在等待锁, 而是可以直接进行相应操作.
5,Redis 分布式锁的缺点
Redis 分布式锁会有个缺陷, 就是在 Redis 哨兵模式下:
客户端 1 对某个 master 节点写入了 redisson 锁, 此时会异步复制给对应的 slave 节点. 但是这个过程中一旦发生 master 节点宕机, 主备切换, slave 节点从变为了 master 节点.
这时客户端 2 来尝试加锁的时候, 在新的 master 节点上也能加锁, 此时就会导致多个客户端对同一个分布式锁完成了加锁.
这时系统在业务语义上一定会出现问题, 导致各种脏数据的产生.
缺陷在哨兵模式或者主从模式下, 如果 master 实例宕机的时候, 可能导致多个客户端同时完成加锁.
说明 这篇博客主要是根据自己的开发经验, 同时也在网上找了许多资料后整理的, 如果哪里有写的不对, 希望多多指点. 万分感激!
只要自己变优秀了, 其他的事情才会跟着好起来 (中将 5)
posted on 2019-06-18 22:44 雨点的名字 阅读 (...) 评论 (...) 编辑 收藏
来源: https://www.cnblogs.com/qdhxhz/p/11046905.html