对不起,网上的那些 Redis 分布式锁的用法都是错的!

JAVA herman 178浏览
公告:“业余草”微信公众号提供免费CSDN下载服务(只下Java资源),关注业余草微信公众号,添加作者微信:xttblog,发送下载链接帮助你免费下载!
本博客日IP超过1800,PV 2600 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog,之前的微信号好友位已满,备注:返现
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
视频教程免费领

Redis 分布式锁的文章,网上一大片。但是大多数的实现,都有存在漏洞!这也正是为什么 Redis 的作者推出 Redlock 的原因之一了。

今天,我来说说,常见的 Redis 分布式锁都有哪些漏洞!

分布式锁的 N 多 Bug,你真的懂吗?

下面这个命令,相信不少网友都见过,它就是我们常见的分布式锁用法命令!

SET xttblog_resource_name xttblog_random_value NX PX 30000

这个命令如果执行成功,则对应客户端就成功获取到了锁,接下来就可以访问共享资源了;而如果这个命令执行失败,则说明获取锁失败。

xttblog_random_value 是由客户端生成的一个随机字符串,它要保证在足够长的一段时间内在所有客户端的所有获取锁的请求中都是唯一的。目的是限制只有获得锁的人才能释放这个锁。

NX 表示只有当 xttblog_resource_name 对应的 key 值不存在的时候才能 SET 成功。这保证了只有第一个请求的客户端才能获得锁,而其它客户端在锁被释放之前都无法获得锁。

PX 30000 表示这个锁有一个 30 秒的自动过期时间。获得这个锁的客户端,操作共享资源不能超过 30 秒。

最后,当客户端操作完共享资源后,释放锁也很关键!

if redis.call("get",KEYS[1]) == ARGV[1] then
     return redis.call("del",KEYS[1])
 else
     return 0
 end

必须利用原子性的命令来释放锁!上面这段 Lua 脚本就是我们常见的释放锁的命令。

注意上面这个 Lua 脚本,它的数组下标是从 1 开始的。过期时间也非常的重要,如果过短,则可能任务还没有执行完,锁就被释放了;过长,如果当前客户端故障,就会导致其他客户端长时间无法获得锁。

以上就是一个完整的互联网版本了。但是这里有一个问题,很多人没提到。假设,

客户端 A 获得了锁。并操作了很长时间,任务未完成,过期时间到了,锁自动释放了。客户端 B 这时候就能获得一个锁,这个时候,可能存在,A 的任务没中断,只是锁过期了,B 获得了锁,也开始操作共享资源了。由此,这个共享资源就不安全了。

xttblog_random_value 随机字符串也很重要,如果有一样的,那么前面 A 获得的锁,由于超时,被释放掉后,B 就可以获取到锁了,这时 A 任务执行完成,由于随机字符串可能存在一直,因此把 B 的锁就给释放掉了!

除此之外,这个锁,虽然被称为分布式锁。但是,如果我的 Redis 节点上挂了一个 Slave,当 Master 节点不可用的时候,系统自动切到 Slave 上(failover)。但由于 Redis 的主从复制(replication)是异步的,这可能导致在 failover 过程中丧失锁的安全性。比如下面的执行序列:

客户端 A 从 Master 获取了锁。

Master 宕机了,存储锁的 key 还没有来得及同步到 Slave 上。

Slave 升级为 Master。

客户端 B 从新的 Master 获取到了对应同一个资源的锁。

由此,共享资源就又不安全了。

所以,Redis 的作者为了统一分布式锁的标准,搞了一个 Redlock,算是 Redis 官方对于实现分布式锁的指导规范,https://redis.io/topics/distlock,但是这个 Redlock 也被国外的一些分布式专家给喷了。因为它也不完美,有“漏洞”。

打个比方,假设 A 获得了锁,这时发生 GC 了,然后 A 开始执行,刚执行一半,锁可能就过期了。其他客户端,就可以获得锁了,共享资源就又不安全了!

Redlock 还有很多效率,正确性等潜在的问题,具体可以查看这篇文章:https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html。

以上,你知道的越多,不知道的就越多,业余的像一棵小草一样。

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加作者微信号1:xmtxtt(5000人已满),微信号2:codedq(5000人已满),微信号3:xttblog(超2800)。备注:“xttblog”,添加博主微信拉你进微信群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作也可添加作者微信进行联系!

本文原文出处:业余草: » 对不起,网上的那些 Redis 分布式锁的用法都是错的!