乐趣区

关于redis:redis分布式锁的这些坑我怀疑你是假的开发

摘要:用锁遇到过哪些问题?

一、文言分布式

什么是分布式,用最简略的话来说,就是为了较低单个服务器的压力,将性能散布在不同的机器下面;

就比方:

原本一个程序员能够实现一个我的项目:需要 -> 设计 -> 编码 -> 测试

然而我的项目多的时候,一个人也扛不住,这就须要不同的人进行分工合作了

这就是一个简略的分布式协同工作了;

二、分布式锁

首先看一个问题,如果说某个环节被终止或者别强占,就会产生不可知的事件

这就会呈现,设计好的或者设计的半成品会被毁坏,导致前面环节出错;

这时候,咱们就须要引入 分布式锁 的概念;

何为分布式锁?

  • 当在分布式模型下,数据只有一份(或有限度),此时须要利用锁的技术管制某一时刻批改数据的过程数。
  • 用一个状态值示意锁,对锁的占用和开释通过状态值来标识。

分布式锁的条件:

  • 能够保障在分布式部署的利用集群中,同一个办法在同一时间只能被一台机器上的一个线程执行。
  • 这把锁要是一把可重入锁(防止死锁)
  • 这把锁最好是一把阻塞锁
  • 这把锁最好是一把偏心锁
  • 有高可用的获取锁和开释锁性能
  • 获取锁和开释锁的性能要好

分布式锁的实现:

​ 分布式锁的实现由很多种,文件锁、数据库、redis 等等,比拟多,在实践中,还是 redis 做分布式锁性能会高一些;

三、redis 实现分布式锁

首先看两个命令:

setnx:将 key 的值设为 value,当且仅当 key 不存在。若给定的 key 曾经存在,则 SETNX 不做任何动作。SETNX 是 SET if Not eXists 的简写。

expire: EXPIRE key seconds

为给定 key 设置生存工夫,当 key 过期时(生存工夫为 0 ),它会被主动删除

基于分布式锁的流程:

这就是一个简略的分布式锁的实现流程,具体代码实现也很简略,就不赘述了;

四、redis 实现分布式锁问题

如果呈现了这么一个问题:如果 setnx 是胜利的,然而 expire 设置失败,那么前面如果呈现了开释锁失败的问题,那么这个锁永远也不会被失去,业务将被锁死?

解决的方法:应用 set 的命令,同时设置锁和过期工夫

set参数:

实际:

这样就完满的解决了分布式锁的原子性;

用锁遇到过哪些问题?又是如何解决的?

未敞开资源

因为以后线程 获取到 redis 锁,解决完业务后未及时开释锁,导致其它线程会始终尝试获取锁阻塞,例如:用 Jedis 客户端会报如下的错误信息

1redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
redis 线程池曾经没有闲暇线程来解决客户端命令。应用原生办法记得敞开!

解决的办法也很简略,只有咱们仔细一点,拿到锁的线程解决完业务及时开释锁

B 的锁被 A 给开释了

咱们晓得 Redis 实现锁的原理在于 SETNX 命令。当 key 不存在时将 key 的值设为 value,返回值为 1;若给定的 key 曾经存在,则 SETNX 不做任何动作,返回值为 0。

SETNX key value

咱们来构想一下这个场景:A、B 两个线程来尝试给 key myLock 加锁,A 线程先拿到锁(如果锁 3 秒后过期),B 线程就在期待尝试获取锁,到这一点故障没有。

那如果此时业务逻辑比拟耗时,执行工夫曾经超过 redis 锁过期工夫,这时 A 线程的锁主动开释(删除 key),B 线程检测到 myLock 这个 key 不存在,执行 SETNX 命令也拿到了锁。

然而,此时 A 线程执行完业务逻辑之后,还是会去开释锁(删除 key),这就导致 B 线程的锁被 A 线程给开释了。

为防止上边的状况,个别咱们在每个线程加锁时要带上本人独有的 value 值来标识,只开释指定 value 的 key,否则就会呈现开释锁凌乱的场景

个别咱们能够设置 value 为业务前缀_以后线程 ID 或者 uuid, 只有以后 value 雷同的才能够开释锁

锁过期了,业务还没执行完

redis 分布式锁过期,而业务逻辑没执行完的场景,不过,这里换一种思路想问题,把 redis 锁的过期工夫再弄长点不就解决了吗?

那还是有问题,咱们能够在加锁的时候,手动调长 redis 锁的过期工夫,可这个工夫多长适合?业务逻辑的执行工夫是不可控的,调的过长又会影响操作性能。

要是 redis 锁的过期工夫可能主动续期就好了。

为了解决这个问题咱们应用 redis 客户端 redisson,redisson 很好的解决了 redis 在分布式环境下的一些辣手问题,它的主旨就是让使用者缩小对 Redis 的关注,将更多精力用在解决业务逻辑上。

redisson 对分布式锁做了很好封装,只需调用 API 即可。

1  RLock lock = redissonClient.getLock("stockLock");

redisson 在加锁胜利后,会注册一个定时工作监听这个锁,每隔 10 秒就去查看这个锁,如果还持有锁,就对过期工夫进行续期。默认过期工夫 30 秒。这个机制也被叫做:“看门狗”

redis 主从复制的坑

redis 高可用最常见的计划就是主从复制(master-slave),这种模式也给 redis 分布式锁挖了一坑。

redis cluster 集群环境下,如果当初 A 客户端想要加锁,它会依据路由规定抉择一台 master 节点写入 key mylock,在加锁胜利后,master 节点会把 key 异步复制给对应的 slave 节点。

如果此时 redis master 节点宕机从节点复制失败,为保障集群可用性,会进行主备切换,slave 变为了 redis master。B 客户端在新的 master 节点上加锁胜利,而 A 客户端也认为本人还是胜利加了锁的。另外如果主从复制提早同样也会造成加锁和解锁提早的问题。

此时就会导致同一时间内多个客户端对一个分布式锁实现了加锁,导致各种脏数据的产生。

毕竟 redis 是放弃的 AP 而非 CP,如果要谋求强一致性能够应用 zookeeper 分布式锁

本文分享自华为云社区《redis 分布式锁?易踩得坑》,原文作者:minjie。

点击关注,第一工夫理解华为云陈腐技术~

退出移动版