手撕redis锁,就这么简单

北京中科白癜风医院科学大讲堂 http://baidianfeng.39.net/a_zhiliao/191126/7642091.html

前言

老猫和小伙伴们分享了为什么要使用分布式锁以及分布式锁的实现思路原理,目前我们主要采用第三方的组件作为分布式锁的工具。运用了Mysql中的select...forupdate实现了分布式锁,但是我们说这种实现方式并不常用,因为当大并发量的时候,会给数据库带来比较大的压力。

当然也有小伙伴给老猫留言说“在quartz的集群模式中,就是使用了基于mysql的分布式锁,selectforupdate”。没错,其实quartz的集群模式中,任务执行的节点个数是可预知的,而且没有那么大的量级,所以是没有问题的。但是如果像千万级别的并发秒杀场景的情况下,那么这种方案其实是不可行的。因为mysql操作是需要IO的,IO的速度比内存速度慢,因此mysql如果在那种场景下使用的话是会存在系统瓶颈的。

所以本篇就和小伙伴们分享基于内存操作的比较常用的分布式锁——redis分布式锁。

手撸Redis分布式锁

实现原理

redis分布式锁实现原理其实也是比较简单的,主要是依赖于redis的setnx命令,我们来看一下完整的设置redis的命令:“Setresource_namemy_random_valueNXPX”。看到这串命令,了解redis的小伙伴应该都看得懂这条命令是在redis中存入一个带有过期时间的值。具体上述设值语句解释如下:

resource_name:资源名称,可以根据不同的业务区分不同的锁。(其实就是对应我们myql锁中的business_code)。my_random_value:随机值,每个线程的随机值都不相同,主要用于释放锁的时候用来校验。NX:key不存在的时候设置成功,key存在则设置不成功。PX:自动失效时间,如果出现异常情况,锁可以过期实现,因此达到了自动释放。那么为什么可以使用这个思路呢?其实很简单,主要就是利用了setnx的原子性,在多个线程并发执行时,只有一个线程可以设置成功,如果设置成功,那么就代表着获得了锁,就可以执行后续的业务。如果出现了异常,过了锁的有效期,锁会自动释放,释放锁主要采用了redis的delete命令,释放锁之前会校验当前redis存储的随机数,只有当前的随机数和存储的随机数一致的时候才允许释放。具体的redis的删除,我们可以通过lua脚本进行删除,具体Lua脚本如下:

ifredis.call(get,KEYS[1])==ARGV[1]thenreturnredis.call(del,KEYS[1])elsereturn0end

那么我们为什么要采用这种方式释放锁呢?其实使用这种方式释放锁可以避免删除别的客户端获取成功的锁。

如下图:

redis释放锁

客户端A取得资源锁,但是紧接着被一个其他操作阻塞了,当客户端A运行完毕其他操作后要释放锁时,原来的锁早已超时并且被Redis自动释放,并且在这期间资源锁又被客户端B再次获取到。如果仅使用DEL命令将key删除,那么这种情况就会把客户端B的锁给删除掉。使用Lua脚本就不会存在这种情况,因为脚本仅会删除value等于客户端A的value的key(value相当于客户端的一个签名)(说明:其实这些例子在redis的


转载请注明:http://www.aierlanlan.com/rzfs/3554.html