Redis做分布式锁真的靠谱吗


Redis的分布式锁可以通过Lua进行实现,通过setnx和expire命令连用的方式 || 也可以使用高版本的方法同时设置失效时间,但是假如在以下情况下,就会造成无锁的现象。

注:分布式锁能不用就不用,尤其是在高并发的情况下。

释放了不该释放的锁:

  有时候直接执行 del mylock 是有问题的,我们不能直接执行 del mylock 为什么?—— 会导致 “信号错误”,释放了不该释放的锁 。

假设如下场景:

时间线 线程1 线程2 线程3
时刻1 执行 setnx mylock val1 加锁 执行 setnx mylock val2 加锁 执行 setnx mylock val2 加锁
时刻2 加锁成功 加锁失败 加锁失败
时刻3 执行任务... 尝试加锁... 尝试加锁...
时刻4 任务继续(锁超时,自动释放了) setnx 获得了锁(因为线程1的锁超时释放了) 仍然尝试加锁...
时刻5 任务完毕,del mylock 释放锁 执行任务中... 获得了锁(因为线程1释放了线程2的)
...      

上面的表格中,有两个维度,一个是纵向的时间线,一个是横线的线程并发竞争。

我们可以发现线程 1 在开始的时候比较幸运,获得了锁,最先开始执行任务,但是,由于他比较耗时,最后锁超时自动释放了他都还没执行完。

因此,线程 2 和线程3 的机会来了。

而这一轮,线程2 比较幸运,得到了锁。

可是,当线程2正在执行任务期间,线程1 执行完了,还把线程2的锁给释放了。

这就相当于,本来你锁着门在厕所里边尿尿,进行到一半的时候,别人进来了,因为他配了一把和你一模一样的钥匙!这就乱套了啊

因此,我们需要安全的释放锁——“不是我的锁,我不能瞎释放”。

所以,我们在加锁的时候,就需要标记“这是我的锁”,在释放的时候在判断 “ 这是不是我的锁?”。

这里就需要在释放锁的时候加上逻辑判断,合理的逻辑应该是这样的:

1. 线程1 准备释放锁 , 锁的key 为 mylock  锁的 value 为 thread1_magic_num
2. 查询当前锁 current_value = get mylock
3. 判断 if current_value == thread1_magic_num -- > 是 我(线程1)的锁
else -- >不是 我(线程1)的锁
4. 是我的锁就释放,否则不能释放(而是执行自己的其他逻辑)。

为了实现上面这个逻辑,我们是无法通过 redis 自带的命令直接完成的。

如果,再写复杂的代码去控制释放锁,则会让整体代码太过于复杂了。

所以,我们引入了lua脚本。

结合Lua 脚本实现释放锁的功能,更简单,redis 执行lua脚本也是原子的,所以更合适,让合适的人干合适的事,岂不更好。

Lua实现分布式锁 :

加锁:

--[[
思路:
1.用2个局部变量接受参数
2.由于redis内置lua解析器,执行加锁命令
3.如果加锁成功,则设置超时时间
4.返回加锁命令的执行结果
]]
local key = KEYS[1]
local value = KEYS[2] local rs1 = redis.call('SETNX',key,value)
if rs1 == true
then
redis.call('SETEX', key,3600, value)
end return rs1

解锁:

--[[
思路:
1.接受redis传来的参数
2.判断是否是自己的锁,是则删掉
3.返回结果值
]]
local key = KEYS[1]
local value = KEYS[2] if redis.call('get',key) == value
then
return redis.call('del',key)
else
return false
end

如此,我们便实现了锁的安全释放。

同时,我们还需要结合业务逻辑,进行具体健壮性的保证,比如如果结束了一定不能忘记释放锁,异常了也要释放锁,某种情况下是否需要回滚事务等。

总结这个分布式锁使用的过程便是:

  • 加锁时 key 同,value 不同。
  • 释放锁时,根据value判断,是不是我的锁,不能释放别人的锁。
  • 及时释放锁,而不是利用自动超时。
  • 锁超时时间一定要结合业务情况权衡,过长,过短都不行。
  • 程序异常之处,要捕获,并释放锁。如果需要回滚的,主动做回滚、补偿。保证整体的健壮性,一致性。

用redis做分布式锁真的靠谱吗???

  上面的文字中,我们讨论如何使用redis作为分布式锁,并讨论了一些细节问题,如锁超时的问题、安全释放锁的问题。

目前为止,似乎很完美的解决的我们想要的分布式锁功能。然而事情并没有这么简单,用redis做分布式锁并不“靠谱”。

不靠谱的情况:

  比如在 Sentinel 集群中,主节点挂掉时,从节点会取而代之,客户端上却并没有明显感知。

原先第一个客户端在主节点中申请成功了一把锁,但是这把锁还没有来得及同步到从节点,主节点突然挂掉了。

然后从节点变成了主节点,这个新的节点内部没有这个锁,所以当另一个客户端过来请求加锁时,立即就批准了。

这样就会导致系统中同样一把锁被两个客户端同时持有,不安全性由此产生。

不过这种不安全也仅仅是在主从发生 failover 的情况下才会产生,而且持续时间极短,业务系统多数情况下可以容忍。

redlock:

为了解决故障转移情况下的缺陷,Antirez 发明了 Redlock 算法,使用redlock算法,需要多个redis实例,加锁的时候,它会想多半节点发送 setex mykey myvalue 命令,只要过半节点成功了,那么就算加锁成功了。

释放锁的时候需要想所有节点发送del命令。

这是一种基于【大多数都同意】的一种机制。

感兴趣的可以查询相关资料。在实际工作中使用的时候,我们可以选择已有的开源实现,python有redlock-py,java 中有Redisson redlock。

为了使用 Redlock,需要提供多个 Redis 实例,这些实例之前相互独立没有主从关系。

同很多分布式算法一样,redlock 也使用「大多数机制」。

加锁时,它会向过半节点发送 set(key, value, nx=True, ex=xxx) 指令,只要过半节点 set成功,那就认为加锁成功。

释放锁时,需要向所有节点发送 del 指令。

不过 Redlock 算法还 需要考虑出错重试、时钟漂移等很多细节问题,同时因为 Redlock 需要向多个节点进行读写,意味着相比单实例 Redis 性能会下降一些。

Redlock 使用场景:

如果你很在乎高可用性,希望挂了一台 redis 完全不受影响,那就应该考虑 redlock。

不过代价也是有的,需要更多的 redis 实例,性能也下降了,代码上还需要引入额外的library,运维上也需要特殊对待,这些都是需要考虑的成本,使用前请再三斟酌。


人生无常大肠包小肠

引文:https://www.cnblogs.com/dalianpai/p/12709408.html

Redis++:Redis做分布式锁真的靠谱吗的更多相关文章

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

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

  2. 程序员修神之路--redis做分布式锁可能不那么简单

    菜菜哥,复联四上映了,要不要一起去看看? 又想骗我电影票,对不对? 呵呵,想去看了叫我呀 看来你工作不饱和呀 哪有,这两天我刚基于redis写了一个分布式锁,很简单 不管你基于什么做分布式锁,你觉得很 ...

  3. 基于 Redis 做分布式锁

    基于 REDIS 的 SETNX().EXPIRE() 方法做分布式锁 setnx() setnx 的含义就是 SET if Not Exists,其主要有两个参数 setnx(key, value) ...

  4. Redis除了做缓存--Redis做消息队列/Redis做分布式锁/Redis做接口限流

    1.用Redis实现消息队列 用命令lpush入队,rpop出队 Long size = jedis.lpush("QueueName", message);//返回存放的数据条数 ...

  5. Redis高并发分布式锁详解

    为什么需要分布式锁 1.为了解决Java共享内存模型带来的线程安全问题,我们可以通过加锁来保证资源访问的单一,如JVM内置锁synchronized,类级别的锁ReentrantLock. 2.但是随 ...

  6. 基于Redis的简单分布式锁的原理

    参考资料:https://redis.io/commands/setnx 加锁是为了解决多线程的资源共享问题.Java中,单机环境的锁可以用synchronized和Lock,其他语言也都应该有自己的 ...

  7. redis客户端、分布式锁及数据一致性

    Redis Java客户端有很多的开源产品比如Redission.Jedis.lettuce等. Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持:Redis ...

  8. 如何用redis正确实现分布式锁?

    先把结论抛出来:redis无法正确实现分布式锁!即使是redis单节点也不行!redis的所谓分布式锁无法用在对锁要求严格的场景下,比如:同一个时间点只能有一个客户端获取锁. 首先来看下单节点下一般r ...

  9. spring boot:用redis+redisson实现分布式锁(redisson3.11.1/spring boot 2.2)

    一,为什么要使用分布式锁? 如果在并发时锁定代码的执行,java中用synchronized锁保证了线程的原子性和可见性 但java锁只在单机上有效,如果是多台服务器上的并发访问,则需要使用分布式锁, ...

随机推荐

  1. C#CancellationToken/CancellationTokenSource-取消令牌/取消令牌源 CT/CTS

    详细情况:https://www.cnblogs.com/wucy/p/15128365.html 背景 为什么引入取消令牌? Thread.abort()方法会破坏同步锁中代码的原子逻辑,破坏锁的作 ...

  2. PostgreSQL-PL/pgSQL控制结构

    PL/pgSQL的控制结构是最重要及最有用的一部分了,在实际工作场景都离不开业务处理逻辑,在写PL/pgSQL时,利用控制结构来操作数据.PL/pgSQL支持的控制结构与其他语言几乎差不多,比如:条件 ...

  3. GNS3与抓包工具Wireshark的关联

    转至:https://blog.51cto.com/xpleaf/1615145 (一)前言 本博文分享GNS3与Wireshark关联的方法. 显然现在网络上已经有类似的文章分享,而本博文旨在提供更 ...

  4. 路径修改后cmd命令行窗口仍然没有变化的原因

    修改环境变量后,要重启cmd再输入才有用

  5. Mysql引擎、隔离机制、存储结构、索引

    目录 数据库常用的两种引擎 两种引擎差异对比 如何选择引擎 两个引擎索引结构 查找mysql数据存储位置方式 MyISAM InnoDB 1. 非独立表空间 2. 独立表空间 3. idb文件存的哪些 ...

  6. 面试官:我们来聊一聊Redis吧,你了解多少就答多少

    哈喽!大家好,我是小奇,一位不靠谱的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新,建议收藏关注 一.前言 作为一名Java程 ...

  7. 4. Java流程控制

    4.Java流程控制 4.1.用户交互Scanner 之前我们学的基本语法中我们并没有实现程序和人的交互,但是Java给我们提供了这样一个工具类,我们可以获取用户的输入.java.util.Scann ...

  8. DDos攻击竟然这么恐怖,它的原理是什么?

    DDOS的定义 分布式拒绝服务(DDoS:Distributed Denial of Service)攻击指借助于客户/服务器技术,将多个计算机联合起来作为攻击平台,对一个或多个目标发动DDoS攻击, ...

  9. redirect route 路由传参

    return redirect()->route('exams.sign',['token'=>$token,'id'=>$result['id']]); // 签到页面 Route ...

  10. 10 Java的方法 可变参数

    5.可变参数 JDK 1.5开始,Java支持传递同类型的可变参数给一个方法. 在方法声明中,在制定参数类型后加一个省略号(-). 一个方法中只能指定一个可变参数,它必须是方法的最后一个参数.任何普通 ...