• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

关于java:Redis实现分布式锁十连问

java 搞代码 4年前 (2022-01-27) 21次浏览 已收录 0个评论
文章目录[隐藏]

前言

分布式锁就是在多个过程之间达到互斥的目标,常见的计划包含:基于DB的惟一索引、Zookeeper的长期有序节点、Redis的SETNX来实现;Redis因为其高性能被宽泛应用,本文通过一问一答的形式来理解Redis如何去实现分布式锁的。

1.Redis怎么实现分布式锁

应用Redis提供的SETNX命令保障只有一次能写入胜利

SETNX key value

当且仅当key不存在,则给key设值为value;若给定的key曾经存在,则什么也不做;

127.0.0.1:6379> setnx lock 001
(integer) 1
127.0.0.1:6379> setnx lock 002
(integer) 0

当然也能够应用SET命令,并应用NX关键字

set <key> <value> NX

2.如果获取锁的节点挂了怎么办

如果仅仅应用SETNX命令,当某个节点抢占到锁,如果这时候以后节点挂了,那么导致这个锁无奈开释,最终会导致死锁呈现;这时候想到的是给key设置一个过期工夫,这样就是节点挂了也会主动删除;

127.0.0.1:6379> expire lock 5
(integer) 1

以上应用expire命令设置过期工夫;

3.如果Set执行完Expire未执行节点挂了

以上问题的起因是因为SETNX命令和Expire不是原子操作,所有有可能在执行完SETNX命令之后节点就挂了,这时候Expire还没来得及执行,同样会导致锁无奈开释,呈现死锁景象;

127.0.0.1:6379> set lock 001 ex 5 nx
OK

如上命令将SETNXExpire命令整合成一个原子操作,保障了同时胜利同时失败;

4.没有获取锁的节点如何阻塞解决

没有获取到锁的节点须要处于阻塞状态,并且定时去重试,保障第一工夫能获取锁;

while(true){
   set lock uuid ex 5 nx;   ## 抢占锁
   if(获取锁){
      break;
   }
   ......
   sleep(1);                ## 避免始终耗费CPU 
}

如果想性能更弱小一点能够指定阻塞工夫,超过指定阻塞工夫就间接获取锁失败;

5.如果解决锁的可重入问题

可重入就是如果某个线程获取了锁,那么以后线程再次获取锁的时候,应该还是能够进入锁中的,每重入一次数量加一,进去时减一;本地能够应用threadId或者间接应用ThreadLocal来实现;当然最好是间接把相干信息保留在Redis中,Redisson应用lua脚本来记录threadId信息:

if (redis.call('exists', KEYS[1]) == 0) then            ## 如果锁不存在
redis.call('hincrby', KEYS[1], ARGV[2], 1);             ## 保留锁,同时设置threadId
redis.call('pexpire', KEYS[1], ARGV[1]);                ## 设置过期工夫
return nil; 
end; 
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then  ## 如果锁存在并且threadId就是以后线程id
redis.call('hincrby', KEYS[1], ARGV[2], 1);             ## 给threadId自增
redis.call('pexpire', KEYS[1], ARGV[1]);                ## 设置过期工夫
return nil; 
end; " 
return redis.call('pttl', KEYS[1]);

6.如果过期工夫到了,工作刚好执行完会怎么样

失常来说咱们预估的过期工夫相对来说都比执行工作的工夫长一些,所以当工作执行完之后会做删除操作

127.0.0.1:6379> del lock
(integer) 1

有没有可能A节点获取的锁过期工夫到了,锁被删除,这时候B节点获取到锁,又从新执行了set ex nx命令;而刚好A节点工作执行实现,并且执行删除锁命令,把B节点的锁给删掉,呈现锁被误删的状况;

这种状况就须要咱们在删除锁的时候,查看以后被删除的锁是否就是咱们之前获取的锁,能够在set的时候执行一个惟一的value,比方间接应用uuid;这样在删除的时候咱们须要先获取锁对应的value值,而后和以后节点对象的value做比拟,统一才能够删除;

string uuid = gen();     ## 生成一个惟一value
set lock uuid ex 5 nx;   ## 抢占锁
......                   ## 执行业务   
string value <span style="color:transparent">来源gaodai#ma#com搞*!代#%^码网</span>= get lock; ## 获取以后锁对应的value值
if(value == uuid) {      ## 比照获取的value值和uuid是否统一
   del lock              ## 统一执行删除操作
} else {
   return;               ## 否则不执行删除操作
}

7.如果过期工夫到了,工作还没执行完怎么办

过期工夫是一个预估的工夫,如果真有某个工作执行的工夫很长,而这时候刚好过期工夫到了,锁就会被删除,导致其余节点又能够获取锁了,这样就呈现了多个节点同时获取锁的状况;

这种状况个别会这么解决:

  • 过期工夫设置的足够长,确保工作能够执行完;
  • 启动一个守护线程,为将要过期但未开释的锁减少工夫,就是给锁续命;

咱们罕用的工具包Redisson,外部提供了一个监控锁的看门狗,它的作用是在Redisson实例被敞开前,一直的缩短锁的有效期;外部应用HashedWheelTimer作为定时器定期检查;

8.Redis主节点宕机,还未同步从节点怎么办

咱们晓得Redis主从同步是异步的,如果某个节点获取了锁,这时候锁信息还未同步到从节点,主节点宕机了,从节点降级为主节点,导致锁失落;这种状况Redis作者提出了redlock算法,大抵含意如下:

在Redis的分布式环境中,假如咱们有N个Redis主机;这些节点是齐全独立的,因而咱们不应用复制或任何其余隐式协调系统;

当且仅当从大多数(N/2+1,这里是3个节点)的Redis节点都取到锁,并且应用的工夫小于锁生效工夫时,锁才算获取胜利。

Redisson提供了RedLock的反对,应用也很简略:

<code class="java">RLock lock1 = redissonClient1.getLock(resourceName); 
RLock lock2 = redissonClient2.getLock(resourceName); 
RLock lock3 = redissonClient3.getLock(resourceName); 
// 向3个redis实例尝试加锁 
RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3); 

更多:redlock

9.Redis呈现集群脑裂会怎么样

集群脑裂指因为网络问题,导致主节点、从节点以及sentinel处于不同的网络分区,因为sentinel的存在会因为某些主节点不存在,而晋升从节点为主节点,这时候就存在了不同的主节点,此时不同的客户端可能连贯不同的主节点,两个客户端能够同时领有同一把锁;

Redis 提供了两个配置项来限度主库的申请解决,别离是 min-slaves-to-writemin-slaves-max-lag

  • min-slaves-to-write:设置了主库能进行数据同步的起码从库数量
  • min-slaves-max-lag:设置了主从库间进行数据复制时,从库给主库发送ACK音讯的最大提早(以秒为单位)

配置项组合后要求主库连贯的从库中至多有 N 个从库、主库进行数据复制时的 ACK 音讯提早不能超过N秒,否则主库就不会再接管客户端的申请。

10.如何实现一个偏心锁

咱们晓得ReentrantLock通过AQS来偏心锁,AQS外部通过双向队列来实现,Redis自身提供了多种数据结构包含列表、有序汇合等;Redisson实现偏心锁正是通过Redis内置的数据结构来实现的:

  • 应用列表作为线程的期待队列,新的期待队列增加到列表的尾部;
  • 应用有序汇合寄存期待线程的程序,分数score是期待线程的超时工夫戳;

总结

不论应用哪种形式去实现分布式锁,咱们前提须要保障锁的性能包含:互斥性、可重入性、阻塞性;同时因为分布式的存在咱们须要保证系统的高可用、高性能、杜绝所有呈现死锁和同时取得锁的状况。


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:关于java:Redis实现分布式锁十连问

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址