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

一、文言分布式

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

就比方:

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

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

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

二、分布式锁

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

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

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

何为分布式锁?

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

分布式锁的条件:

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

分布式锁的实现:

分布式锁的实现由很多种,文件锁、数据库、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 。

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