来自:掘金(作者:Vt 编辑:陶家龙)
原文链接:
https://juejin.im/post/5e61a454e51d4526f071e1df
Setnx
RedLock
Redisson
Setnx
一般代指 Redis 中对 Set 命令加上 NX 参数进行使用,Set 这个命令,目前已经支持这么多参数可选:
SET key value [EX seconds|PX milliseconds] [NX|XX] [KEEPTTL]
当然了,就不在文章中默写 API 了,基础参数还有不清晰的,可以蹦到官网。
那么为什么要使用 PX 30000 去设置一个超时时间?是怕进程 A 不讲道理啊,锁没等释放呢,万一崩了,直接原地把锁带走了,导致系统中谁也拿不到锁。
就算这样,还是不能保证万无一失。如果进程 A 又不讲道理,操作锁内资源超过笔者设置的超时时间,那么就会导致其他进程拿到锁,等进程 A 回来了,回手就是把其他进程的锁删了,如图:
当进程 B 操作完成,去释放锁的时候(图中 T8 时刻):
当解锁的时候,先获取 Value 判断是否是当前进程加的锁,再去删除。伪代码:
String uuid = xxxx;
// 伪代码,具体实现看项目中用的连接工具
// 有的提供的方法名为set 有的叫setIfAbsent
set Test uuid NX PX 3000
try{
// biz handle....
} finally {
// unlock
if(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
-- 不成功,返回0
return 0
end
1. setnx Test uuid
2. expire Test 30
如果有缘你也阅读过这篇文章,并且学到了这个套路,作为本文的笔者我要加一句提醒:请注意你的工作年限!首先回答官网表明即将废弃的命令,再引出 Set 命令七年前的“新特性”,如果是刚毕业不久的人这么说,面试官会以为自己穿越了。
你套路面试官,面试官也会套路你。
-- vt・沃兹基硕德
Redisson
但是 Redisson 这个客户端可有点厉害,笔者在官网截了仅仅是一部分的图:
笔者也非常严谨的思考了一下:这么厉害的东西哪能写废代码?
其实笔者仔细看了一下,加锁解锁的 Lua 脚本考虑的非常全面,其中就包括锁的重入性,这点可以说是考虑非常周全,我也随手写了代码测试一下:
RedLock
笔者大概画了一下对红锁的理解:
顺序向五个节点请求加锁
根据一定的超时时间来推断是不是跳过该节点
三个节点加锁成功并且花费时间小于锁的有效期
认定加锁成功
回头看看 Redis 官网关于红锁的描述,就在这篇描述页面的最下面,你能看到著名的关于红锁的神仙打架事件。
Martin Kleppmann 的质疑贴
Antirez 的反击贴
总结
欢迎关注公众号『easyserverdev』,本公众号推崇基础学习与原理理解,不谈大而空的架构与技术术语,分享接地气的服务器开发实战技巧与项目经验,实实在在分享可用于实际编码的编程知识。如果对后端开发感兴趣,想加入 高性能服务器开发微信交流群 进行交流,可以先加我微信 easy_coder,备注"加微信群",我拉你入群,备注不对不加哦。
好文章,我 在看
本文分享自微信公众号 - 高性能服务器开发(easyserverdev)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
来源:oschina
链接:https://my.oschina.net/u/3318202/blog/4403715