点击上方“码猿技术专栏”关注,选择“设为星标”

正文-开门见山

谈起redis锁,下面三个,算是出现最多的高频词汇:

  • setnx

  • redLock

  • redisson

setnx

其实目前通常所说的setnx命令,并非单指redis的 setnx key value这条命令。

一般代指redis中对set命令加上nx参数进行使用, set这个命令,目前已经支持这么多参数可选:

SET key value[EX seconds|PX milliseconds][NX|XX][KEEPTTL]

当然了,就不在文章中默写Api了,基础参数还有不清晰的,可以蹦到官网。

上图是笔者画的setnx大致原理,主要依托了它的key不存在才能set成功的特性,进程A拿到锁,在没有删除锁的Key时,进程B自然获取锁就失败了。

那么为什么要使用 PX30000去设置一个超时时间?

是怕进程A不讲道理啊,锁没等释放呢,万一崩了,直接原地把锁带走了,导致系统中谁也拿不到锁。

就算这样,还是不能保证万无一失。

如果进程A又不讲道理,操作锁内资源超过笔者设置的超时时间,那么就会导致其他进程拿到锁,等进程A回来了,回手就是把其他进程的锁删了,如图:

还是刚才那张图,将 T5时刻改成了锁超时,被redis释放。

进程BT6开开心心拿到锁不到一会,进程A操作完成,回手一个del,就把锁释放了。

进程B操作完成,去释放锁的时候(图中T8时刻):

找不到锁其实还算好的,万一T7时刻有个进程C过来加锁成功,那么进程B就把进程C的锁释放了。
以此类推,进程C可能释放进程D的锁,进程D....(禁止套娃),具体什么后果就不得而知了。

所以在用setnx的时候,key虽然是主要作用,但是value也不能闲着,可以设置一个唯一的客户端ID,或者用UUID这种随机数。

当解锁的时候,先获取value判断是否是当前进程加的锁,再去删除。伪代码

String uuid = xxxx;
// 伪代码,具体实现看项目中用的连接工具
// 有的提供的方法名为set 有的叫setIfAbsent
setTest uuid NX PX 3000
try{
// biz handle....
} finally{// unlockif(uuid.equals(redisTool.get('Test')){redisTool.del('Test');}
}

这回看起来是不是稳了。

相反,这回的问题更明显了,在finally代码块中,get和del并非原子操作,还是有进程安全问题。

为什么有问题还说这么多呢?

第一,搞清劣势所在,才能更好的完善。

第二点,其实上文中最后这段代码,还是有很多公司在用的。

大小项目悖论:大公司实现规范,但是小司小项目虽然存在不严谨,可并发倒也不高,出问题的概率和大公司一样低。-- 鲁迅

那么删除锁的正确姿势之一,就是可以使用lua脚本,通过redis的eval/evalsha命令来运行:

-- lua删除锁:
-- KEYS和ARGV分别是以集合方式传入的参数,对应上文的Test和uuid。
-- 如果对应的value等于传入的uuid。
if redis.call('get', KEYS[1]) == ARGV[1]then -- 执行删除操作return redis.call('del', KEYS[1])else -- 不成功,返回0return0
end

通过lua脚本能保证原子性的原因说的通俗一点:

就算你在lua里写出花,执行也是一个命令(eval/evalsha)去执行的,一条命令没执行完,其他客户端是看不到的。

那么既然这么麻烦,有没有比较好的工具呢?就要说到redisson了。


介绍redisson之前,笔者简单解释一下为什么现在的setnx默认是指set命令带上nx参数,而不是直接说是setnx这个命令。

因为redis版本在2.6.12之前,set是不支持nx参数的,如果想要完成一个锁,那么需要两条命令:

1. setnx Test uuid
2. expire Test30

即放入Key和设置有效期,是分开的两步,理论上会出现1刚执行完,程序挂掉,无法保证原子性。

但是早在2013年,也就是7年前,Redis就发布了2.6.12版本,并且官网(set命令页),也早早就说明了“SETNX, SETEX, PSETEX可能在未来的版本中,会弃用并永久删除”。

笔者曾阅读过一位大佬的文章,其中就有一句指导入门者的面试小套路,具体文字忘记了,大概意思如下:

说到redis锁的时候,可以先从setnx讲起,最后慢慢引出set命令的可以加参数,可以体现出自己的知识面。

如果有缘你也阅读过这篇文章,并且学到了这个套路,作为本文的笔者我要加一句提醒:

请注意你的工作年限!首先回答官网表明即将废弃的命令,再引出set命令七年前的“新特性”,如果是刚毕业不久的人这么说,面试官会以为自己穿越了。

你套路面试官,面试官也会套路你。-- vt・沃兹基硕德

redisson

Redisson是java的redis客户端之一,提供了一些api方便操作redis。

但是redisson这个客户端可有点厉害,笔者在官网截了仅仅是一部分的图:

这个特性列表可以说是太多了,是不是还看到了一些JUC包下面的类名,redisson帮我们搞了分布式的版本,比如AtomicLong,直接用RedissonAtomicLong就行了,连类名都不用去新记,很人性化了。

锁只是它的冰山一角,并且从它的wiki页面看到,对主从,哨兵,集群等模式都支持,当然了,单节点模式肯定是支持的。

本文还是以锁为主,其他的不过多介绍。

Redisson普通的锁实现源码主要是RedissonLock这个类,还没有看过它源码的盆友,不妨去瞧一瞧。

源码中加锁/释放锁操作都是用lua脚本完成的,封装的非常完善,开箱即用。

这里有个小细节,加锁使用setnx就能实现,也采用lua脚本是不是多此一举?笔者也非常严谨的思考了一下:这么厉害的东西哪能写废代码?

其实笔者仔细看了一下,加锁解锁的lua脚本考虑的非常全面,其中就包括锁的重入性,这点可以说是考虑非常周全,我也随手写了代码测试一下:

的确用起来像jdk的ReentrantLock一样丝滑,那么redisson实现的已经这么完善,redLock又是什么?

RedLock

redLock的中文是直译过来的,就叫红锁

红锁并非是一个工具,而是redis官方提出的一种分布式锁的算法

就在刚刚介绍完的redisson中,就实现了redLock版本的锁。也就是说除了getLock方法,还有getRedLock方法。

笔者大概画了一下对红锁的理解:

如果你不熟悉redis高可用部署,那么没关系。redLock算法虽然是需要多个实例,但是这些实例都是独自部署的,没有主从关系。

RedLock作者指出,之所以要用独立的,是避免了redis异步复制造成的锁丢失,比如:主节点没来的及把刚刚set进来这条数据给从节点,就挂了。

有些人是不是觉得大佬们都是杠精啊,天天就想着极端情况。其实高可用嘛,拼的就是99.999...% 中小数点后面的位数。

回到上面那张简陋的图片,红锁算法认为,只要(N/2) + 1个节点加锁成功,那么就认为获取了锁, 解锁时将所有实例解锁。流程为:

1、 顺序向五个节点请求加锁 2、 根据一定的超时时间来推断是不是跳过该节点 3、 三个节点加锁成功并且花费时间小于锁的有效期 4、 认定加锁成功

也就是说,假设锁30秒过期,三个节点加锁花了31秒,自然是加锁失败了。

这只是举个例子,实际上并不应该等每个节点那么长时间,就像官网所说的那样,假设有效期是10,那么单个redis实例操作超时时间,应该在5到50毫秒(注意时间单位)

还是假设我们设置有效期是30秒,图中超时了两个redis节点。
那么加锁成功的节点总共花费了3秒,所以锁的实际有效期是小于27秒的。

即扣除加锁成功三个实例的3秒,还要扣除等待超时redis实例的总共时间。

看到这,你有可能对这个算法有一些疑问,那么你不是一个人。

回头看看Redis官网关于红锁的描述

https://redis.io/topics/distlock

即Martin Kleppmann和antirez的redLock辩论. 一个是很有资历的分布式架构师,一个是redis之父。

官方挂人,最为致命。

开个玩笑,要是质疑能被官方挂到官网,说明肯定是有价值的。

所以说如果项目里要使用红锁,除了红锁的介绍,不妨要多看两篇文章,即:

1、 Martin Kleppmann的质疑贴 2、 antirez的反击贴

总结

看了这么多,是不是发现如何实现,都不能保证100%的稳定。

程序就是这样,没有绝对的稳定,所以做好人工补偿环节也是重要的一环,毕竟:

技术不够,人工来凑~

作者 | Vt

来源 | juejin.im/post/5e61a454e51d4526f071e1df

往期推荐

Nginx 为什么快到根本停不下来?面试:HashMap 夺命二十一问!JAVA 线上故障排查完整套路!牛掰!一文入门jvm虚拟机Lombok天天用,却不知道它的原理是什么?

面试官:删除List的姿势知道几种?

面试难题:Redis 分布式锁,真的完美无缺吗?相关推荐

  1. 大家所推崇的Redis分布式锁真的就万无一失吗?

    在单实例JVM中,常见的处理并发问题的方法有很多,比如synchronized关键字进行访问控制.volatile关键字.ReentrantLock等常用方法.但是在分布式环境中,上述方法却不能在跨J ...

  2. 面试官:Redis分布式锁解决方案是什么?

    今天博主在这片文章中主要给大家讲下Redis分布式锁的原理以及解决方案 学到三连呦 1.Redis分布式锁原理 1.1.简述 我们知道分布式锁的特性是排他.避免死锁.高可用.分布式锁的实现可以通过数据 ...

  3. 面试热点Redis分布式锁,再细说一次

    欢迎关注方志朋的博客,回复"666"获面试宝典 谈起redis锁,下面三个,算是出现最多的高频词汇: setnx redLock redisson | setnx 其实目前通常所说 ...

  4. redis订阅执行一段时间自动停止_面试系列 redis 分布式锁amp;数据一致性

    分布式锁 多个系统同时操作一个redis,因为jvm锁是线程级别的,所以没有办法锁住多个系统. Redis锁实现: setnx key value 只有在key不存在时设置key的值 此时key相当于 ...

  5. 深度剖析:Redis分布式锁到底安全吗?看完这篇文章彻底懂了!

    ‍‍‍‍‍‍‍‍‍‍‍‍阅读本文大约需要 20 分钟. 大家好,我是 Kaito. 这篇文章我想和你聊一聊,关于 Redis 分布式锁的「安全性」问题. Redis 分布式锁的话题,很多文章已经写烂了 ...

  6. 深度剖析:Redis 分布式锁到底安全吗?看完这篇文章彻底懂了!

    作者 | Kaito 来源 | 水滴与银弹 阅读本文大约需要 20 分钟. 大家好,我是 Kaito. 这篇文章我想和你聊一聊,关于 Redis 分布式锁的「安全性」问题. Redis 分布式锁的话题 ...

  7. 万字长文剖析Redis分布式锁到底安不安全

    ‍‍‍‍‍‍‍‍‍‍‍‍阅读本文大约需要 20 分钟. 这篇文章我想和你聊一聊,关于 Redis 分布式锁的「安全性」问题. Redis 分布式锁的话题,很多文章已经写烂了,我为什么还要写这篇文章呢? ...

  8. 基于Redis的分布式锁真的安全吗?

    说明: 我前段时间写了一篇用consul实现分布式锁,感觉理解的也不是很好,直到我看到了这2篇写分布式锁的讨论,真的是很佩服作者严谨的态度, 把这种分布式锁研究的这么透彻,作者这种技术态度真的值得我好 ...

  9. 面试官问我,Redis分布式锁如何续期?懵了。

    作者:肥朝,来自:肥朝(ID:feichao_java) 前言 上一篇[面试官问我,使用Dubbo有没有遇到一些坑?我笑了.]之后,又有一位粉丝和我说在面试过程中被虐了.鉴于这位粉丝是之前肥朝的老粉丝 ...

  10. redis desktop manager_面试官:Redis分布式锁如何解决锁超时问题?

    Java面试笔试面经.Java技术每天学习一点 Java面试 关注不迷路 作者:wangzaiplus 来源:https://www.jianshu.com/u/8cb4591440ca 一.前言 关 ...

最新文章

  1. Java中的String类
  2. $unit编译单元声明
  3. Scrapy源码阅读分析_5_Scrapy-settings源码分析
  4. Hibernate笔记2
  5. Spring Boot 推荐的基础 POM 文件
  6. Activity管理类,随时随地退出应用程序
  7. AI算法工程师的一些含泪经验
  8. Bugku 社工——初步收集
  9. ferguson博弈_博弈 - xiaoguozi's Blog - C++博客
  10. 卡巴斯基7.0最新激活码
  11. unity shader相关工具教程
  12. Flutter isolate使用
  13. GDR(Gradual Decoding Refresh, GradualDecoder Refresh)
  14. PHP实现图片上传功能
  15. [ROC-RK3566-PC] 如何编译Linux_SDK并打包Ubuntu系统固件?
  16. YOLO V6系列(一) -- 跑通YOLO V6算法
  17. border:none以及border:0的区别
  18. mysql计算两个月份之间的差值_MySQL计算两个日期相差的天数、月数、年数
  19. 那周余嘉熊掌将得队 团队团队展示
  20. 国脉科技股份有限公司

热门文章

  1. ifreq ifr 配置网口信息
  2. 使用 Petalinux 定制 Linux 系统
  3. android和MTKP60哪个好,联发科P60和骁龙636哪个好?骁龙636与联发科P60区别对比详细评测...
  4. 基于python的硬币面值识别_列出给定数量的python需要什么硬币
  5. Vue项目性能优化篇
  6. BJT管及其放大电路
  7. 计算机基础四化教学,“四化”入课堂,教改出成果——新课改下计算机应用基础的改革实践与创新.doc...
  8. 【云计算入门3】并行互连网络、并行访存模型、当代并行机硬件架构
  9. 无线智能插座MQTT服务端的开启及检验
  10. php定义上传文件格式,对应PHP文件上传后缀名与文件类型