当前位置: 首页 > 科技观察

七种选择!浅谈Redis分布式锁的正确使用姿势

时间:2023-03-12 18:32:53 科技观察

前言在日常开发中,秒下单、抢红包等业务场景,都需要用到分布式锁。而Redis非常适合作为分布式锁来使用。本文将分为七个解决方案来讨论Redis分布式锁的正确使用。如果有不对的地方,欢迎指出,大家一起学习,一起进步。公众号:《捡蜗牛的小男孩》什么是分布式锁方案一:SETNX+EXPIRE方案二:SETNX+value为(系统时间+过期时间)方案三:使用Lua脚本(含SETNX+EXPIRE指令)方案四:SET扩展命令(SETEXPXNX)方案五:SETEXPXNX+验证唯一随机值,然后释放锁方案六:开源框架~Redisson方案七:分布式锁Redlock的多机实现什么是分布式锁?分布式锁实际上是一种锁的实现,它控制分布式系统中的不同进程共同访问共享资源。如果不同的系统或者同一系统的不同主机共享某个关键资源,往往需要互斥来防止相互干扰,保证一致性。我们先来看看可靠的分布式锁的特点:“互斥”:任何时候,只有一个客户端可以持有锁。“锁超时释放”:超时后可以释放锁,防止不必要的资源浪费和死锁。“重入”:如果一个线程获得了锁,它可以再次请求锁。“高性能高可用”:加锁和解锁需要尽可能低的开销,同时保证高可用,避免分布式锁失效。“安全”:锁只能被持有它的客户端删除,其他客户端不能删除。Redis分布式锁方案一:SETNX+EXPIRE一提到Redis的分布式锁,很多朋友马上就会想到setnx+expire命令。即先用setnx抢锁,然后用expire给锁设置一个过期时间,防止锁忘记释放。SETNX是SETIFNOTEXISTS的缩写。每日命令格式为SETNX键值。如果key不存在,SETNX成功返回1,如果key已经存在,则返回0。假设某电商网站的商品在做闪购,key可以设置为key_resource_id,value可以设置为任意值。伪代码如下:if(jedis.setnx(key_resource_id,lock_value)==1){//lockexpire(key_resource_id,100);//设置过期时间try{dosomething//业务请求}catch(){}finally{jedis.del(key_resource_id);//释放锁}}但是在这个方案中,setnx和expire这两个命令是分开的,“不是原子操作”。如果执行了setnx锁,在即将执行expire设置过期时间的时候,进程崩溃或者重启维护,那么这个锁就会“永生”,“其他线程永远得不到锁”。Redis分布式锁方案2:SETNX+valuevalue为(系统时间+过期时间)。针对方案一,“异常锁无法释放的场景”,有朋友认为可以将过期时间放在setnxvalue里面的value中。如果加锁失败,就把值取出来重新校验。锁代码如下:longexpires=System.currentTimeMillis()+expireTime;//系统时间+设置过期时间StringexpiresStr=String.valueOf(expires);//如果当前锁不存在,返回锁成功if(jedis.setnx(key_resource_id,expiresStr)==1){returntrue;}//如果锁已经存在,获取锁的过期时间StringcurrentValueStr=jedis.get(key_resource_id);//如果获取的过期时间小于系统当前时间,表示已经过期if(currentValueStr!=null&&Long.parseLong(currentValueStr)30ms+40ms+50ms+4m0s+50ms)如果获取到锁,key真正生效的时间会发生变化,需要减去获取锁所用的时间。如果获取锁失败(至少有N/2+1个master实例没有获取到锁,或者获取锁时间已经超过有效时间),client必须解锁所有master节点(即使部分master节点没有有锁成功的还需要解锁,防止有人漏网)。以下简化步骤为:依次向5个主节点请求锁。根据设置的超时时间判断是否跳过主节点。如果成功加锁的节点大于等于3个,且使用时间小于锁的有效期,则可以判定加锁成功。如果获取锁失败,请解锁!Redisson已经实现了redLock版本的锁。有兴趣的可以去了解一下~参考并感谢redis系列:分布式锁[1]Redis分布式锁解决方案浅析[2]再说说Redis分布式锁??[3]Redlock:最强大的Redis分布式锁实现转载本文请联系捡蜗牛的小男孩公众号。