Redisson 分布式锁源码 09:RedLock 红锁的故事
前言
RedLock 红锁,是分布式锁中必须要了解的一个概念。
所以本文会先介绍什么是 RedLock,当大家对 RedLock 有一个基本的了解。然后再看 Redisson 中是如何实现 RedLock 的。
在文章开头先说明 Redisson RedLock 建议不要使用!!!
在文章开头先说明 Redisson RedLock 建议不要使用!!!
在文章开头先说明 Redisson RedLock 建议不要使用!!!
重要的事情重复三遍!
什么是 RedLock?
RedLock,这块可以从网上搜到很多资料,本文也简单介绍下,当做扫盲。
单实例加锁
SET resource_name my_random_value NX PX 30000
对于单实例 Redis 只需要使用这个命令即可。
- NX:仅在不存在 key 的时候才能被执行成功;
- PX:失效时间,传入 30000,就是 30s 后自动释放锁;
- my_random_value:就是随机值,可以是线程号之类的。主要是为了更安全的释放锁,释放锁的时候使用脚本告诉 Redis: 只有 key 存在并且存储的值和我指定的值一样才能删除成功。
可以通过以下 Lua 脚本实现锁释放:
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
为什么要设置随机值?
主要是为了防止锁被其他客户端删除。有这么一种情况:
- 客户端 A 获得了锁,还没有执行结束,但是锁超时自动释放了;
- 客户端 B 此时过来,是可以获得锁的,加锁成功;
- 此时,客户端 A 执行结束了,要去释放锁,如果不对比随机值,就会把客户端 B 的锁给释放了。
当然前面看过 Redisson 的处理,这个 my_random_value 存放的是 UUID:ThreadId 组合成的一个类似 931573de-903e-42fd-baa7-428ebb7eda80:1 的字符串。
当锁遇到故障转移
单实例肯定不是很可靠吧?加锁成功之后,结果 Redis 服务宕机了,这不就凉凉~
这时候会提出来将 Redis 主从部署。即使是主从,也是存在巧合的!
主从结构中存在明显的竞态:
- 客户端 A 从 master 获取到锁
- 在 master 将锁同步到 slave 之前,master 宕掉了。
- slave 节点被晋级为 master 节点
- 客户端 B 取得了同一个资源被客户端 A 已经获取到的另外一个锁。安全失效!
有时候程序就是这么巧,比如说正好一个节点挂掉的时候,多个客户端同时取到了锁。如果你可以接受这种小概率错误,那用这个基于复制的方案就完全没有问题。
那我使用集群呢?
如果还记得前面的内容,应该是知道对集群进行加锁的时候,其实是通过 CRC16 的 hash 函数来对 key 进行取模,将结果路由到预先分配过 slot 的相应节点上。
发现其实还是发到单个节点上的!
RedLock 概念
这时候 Redis 作者提出了 RedLock 的概念

总结一下就是对集群的每个节点进行加锁,如果大多数(N/2+1)加锁成功了,则认为获取锁成功。
RedLock 的问题
看着 RedLock 好像是解决问题了:
- 客户端 A 锁住了集群的大多数(一半以上);
- 客户端 B 也要锁住大多数;
- 这里肯定会冲突,所以 客户端 B 加锁失败。
那实际解决问题了么?
推荐大家阅读两篇文章:
Martin Kleppmann:How to do distributed locking
Salvatore(Redis 作者):Is Redlock safe?
最终,两方各持己见,没有得出结论。
鉴于本文主要是分析 Redisson 的 RedLock,就不做额外赘述,感兴趣的小伙伴可以自己阅读。
Redisson 中 RedLock 源码
这里会简要分析一下 Redisson 中 RedLock 的源码,然后会介绍为什么文章开头不建议大家使用 Redisson 的 RedLock。
使用方式

乍一看,感觉和联锁 MultiLock 的使用方式很像啊!

实际上就是很像,RedissonRedLock 完全是 RedissonMultiLock 的子类嘛!
只不过是重写 failedLocksLimit 方法。
在 MultiLock 中,要所有的锁都锁成功才可以。
在 RedLock 中,要一半以上的锁成功。
剩余部分源码都和 MultiLock 一样,就不在重复描述了。
Redisson 中 RedLock 的问题
1、加锁 key 的问题
阅读源码之前,有一个很大的疑问,我加锁 lock1、lock2、lock3,但是 RedissonRedLock 是如何保证这三个 key 是在归属于 Redis 集群中不同的 master 呢?
因为按照 RedLock 的理论,是需要在半数以上的 master 节点加锁成功。
阅读完源码之后,发现 RedissonRedLock 完全是 RedissonMultiLock 的子类,只是重写了 failedLocksLimit 方法,保证半数以上加锁成功即可。
所以这三个 key,是需要用户来保证分散在不同的节点上的。

在 Redisson 的 issues 也有同样的小伙伴提出这个问题,相关开发者给出的回复是用户来保证 key 分散在不同的 master 上。
更有小伙伴提出使用 5 个客户端。

那我使用 5 个单节点的客户端,然后再使用红锁,听着好像是可以的,并且 RedissonRedLock 可以这样使用。
但是那和 Redis 集群还有啥关系啊!
所以依然没有解决我的问题,还是需要用户自己来“手工定位锁”。
手工定位锁,这个…… 我考虑了下,还是不用 RedLock 吧!
当然 DarrenJiang1990 同学应该是怀着打破砂锅问到底的心情,又来了一篇 issue。
https://github.com/redisson/redisson/issues/2437
意思就是:不要关闭我的 issues,在 #2436 中说可以“手工定位锁”,但是我要怎么手工定位锁。
后来这个 issue 在 10 月才回复。

2、RedissonRedLock 被弃用
是的,没有看错,现在 RedissonRedLock 已经被启用了。
如果是看的英文文档,就会发现:

而中文文档,应该是没有及时更新。
来看看更新记录:

再找一找 issue:

Redisson 的开发者认为 Redis 的红锁也存在争议(前文介绍的那个争议),但是为了保证可用性,RLock 对象执行的每个 Redis 命令执行都通过 Redis 3.0 中引入的 WAIT 命令进行同步。
WAIT 命令会阻塞当前客户端,直到所有以前的写命令都成功的传输并被指定数量的副本确认。如果达到以毫秒为单位指定的超时,则即使尚未达到指定数量的副本,该命令也会返回。
WAIT 命令同步复制也并不能保证强一致性,不过在主节点宕机之后,只不过会尽可能的选择最佳的副本(slaves)

源码在这一部分。

看源码,同时发送了一个 WAIT 1 1000 到 Redis。
结论
Redisson RedLock 是基于联锁 MultiLock 实现的,但是使用过程中需要自己判断 key 落在哪个节点上,对使用者不是很友好。
Redisson RedLock 已经被弃用,直接使用普通的加锁即可,会基于 wait 机制将锁同步到从节点,但是也并不能保证一致性。仅仅是最大限度的保证一致性。
相关推荐
Redisson 分布式锁源码 09:RedLock 红锁的故事的更多相关文章
- Redisson 分布式锁源码 11:Semaphore 和 CountDownLatch
前言 Redisson 除了提供了分布式锁之外,还额外提供了同步组件,Semaphore 和 CountDownLatch. Semaphore 意思就是在分布式场景下,只有 3 个凭证,也就意味着同 ...
- Redisson 分布式锁源码 02:看门狗
前言 说起 Redisson,比较耳熟能详的就是这个看门狗(Watchdog)机制. 本文就一起看看加锁成功之后的看门狗(Watchdog)是如何实现的? 加锁成功 在前一篇文章中介绍了可重入锁加锁的 ...
- Redisson分布式锁学习总结:可重入锁 RedissonLock#lock 获取锁源码分析
原文:Redisson分布式锁学习总结:可重入锁 RedissonLock#lock 获取锁源码分析 一.RedissonLock#lock 源码分析 1.根据锁key计算出 slot,一个slot对 ...
- RedissonLock分布式锁源码分析
最近碰到的一个问题,Java代码中写了一个定时器,分布式部署的时候,多台同时执行的话就会出现重复的数据,为了避免这种情况,之前是通过在配置文件里写上可以执行这段代码的IP,代码中判断如果跟这个IP相等 ...
- ReentrantLock之非公平锁源码分析
本文分析的ReentrantLock所对应的Java版本为JDK8. 在阅读本文前,读者应该知道什么是CAS.自旋. 由于ReentrantLock的公平锁和非公平锁中有许多共同代码,本文只会对这两种 ...
- Java关于ReentrantLock获取锁和释放锁源码跟踪
通过对ReentrantLock获取锁和释放锁源码跟踪主要想进一步深入学习AQS. 备注:AQS中的waitStatus状态码含义:
- ReentrantLock 的公平锁源码分析
ReentrantLock 源码分析 以公平锁源码解析为例: 1:数据结构: 维护Sync 对象的引用: private final Sync sync; Sync对象继承 AQS, Syn ...
- v79.01 鸿蒙内核源码分析(用户态锁篇) | 如何使用快锁Futex(上) | 百篇博客分析OpenHarmony源码
百篇博客分析|本篇为:(用户态锁篇) | 如何使用快锁Futex(上) 进程通讯相关篇为: v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志 v27.05 鸿蒙内核源码分析(互斥锁) ...
- v80.01 鸿蒙内核源码分析(内核态锁篇) | 如何实现快锁Futex(下) | 百篇博客分析OpenHarmony源码
百篇博客分析|本篇为:(内核态锁篇) | 如何实现快锁Futex(下) 进程通讯相关篇为: v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志 v27.05 鸿蒙内核源码分析(互斥锁) ...
随机推荐
- C语言关于指针函数与函数指针个人理解
1,函数指针 顾名思义,即指向函数的指针,功能与其他指针相同,该指针变量保存的是所指向函数的地址. 假如是void类型函数指针定义方式可以是 void (*f)(参数列表);亦可以先用 typedef ...
- SpringBoot基础学习(二) SpringBoot全局配置文件及配置文件属性值注入
全局配置文件 全局配置文件能够对一些默认配置值进行修改.SpringBoot 使用一个名为 application.properties 或者 application.yaml的文件作为全局配置文件, ...
- GO学习-(27) Go语言操作NSQ
Go语言操作NSQ NSQ是目前比较流行的一个分布式的消息队列,本文主要介绍了NSQ及Go语言如何操作NSQ. NSQ NSQ介绍 NSQ是Go语言编写的一个开源的实时分布式内存消息队列,其性能十分优 ...
- AI框架类FAQ
AI框架类FAQ 数据处理 问题:如何在训练过程中高效读取数量很大的数据集? 答复:当训练时使用的数据集数据量较大或者预处理逻辑复杂时,如果串行地进行数据读取,数据读取往往会成为训练效率的瓶颈.这种情 ...
- pytorch空间变换网络
pytorch空间变换网络 本文将学习如何使用称为空间变换器网络的视觉注意机制来扩充网络.可以在DeepMind paper 阅读更多有关空间变换器网络的内容. 空间变换器网络是对任何空间变换的差异化 ...
- 嵌入式C程序基础与编程结构
嵌入式C程序基础与编程结构 Basics of Embedded C Program and Programming Structure 嵌入式C编程是处理器在我们日常生活中遇到的每一个嵌入式系统(如 ...
- Python集合:set
集合 集合的描述 set是一个无序不重复的序列,可以用{}或者 set() 函数创建集合,它存放不可变类型(如字符串.数字.元组)数据. 注意:创建一个空集合必须使用set()方法,因为{}是用来生成 ...
- 简单的Java面向对象程序
上一篇随笔Java静态方法和实例方法的区别以及this的用法,老师看了以后说我还是面向过程的编程,不是面向对象的编程,经过修改以后,整了一个面向对象的出来: /** * 3 延续任务2, 定义表示圆形 ...
- CENTOS7 安装 SYNCTHING
本地电脑需要同步远程数据,安装syncthing 测试 1:下载 wget https://github.com/syncthing/syncthing/releases/download/v1.5. ...
- Centos 安装 Influxdb + Chronograf
安装 Influxdb 1:下载安装包 官网下载地址 https://portal.influxdata.com/downloads/# wget https://dl.influxdata.com ...