redis分布式锁
今天记录一下redis实现分布式锁,写这个话题我犹豫了很久,因为这个实现虽然很容易,但是有很多细节需要注意,一不小心就死锁,但是仔细一想好像也没几个人看我的文章,就当给自己做个笔记吧!大佬们要是发现那儿思路有问题,期待你能指出来哦,在生产环境下最好用redisson,别傻傻的自己实现了
在单机下我们的多线程争抢资源是很好解决的,无非就是加锁,Reentrantlock,synchronized等等都行,但是在多线程的情况下,他们已经不是同一个JVM了,他们是不可能在自己的JVM锁到同一个对象的,如图:
所以,在分布式系统中我们需要的是这样一个架构:
这儿的中间件我们今天就是用的redis,后面可能会再写一下zookeeper实现分布式锁
我们只需要知道的几个很简单的redis命令:
set 示例: set name xiaoZ EX 5 NX 解释: 设置name字段的值为xiaoZ,失效时间为5秒,如果不存在这个key的话
expire 示例: expire name 5 解释: 设置name字段的超时时间为5秒
del 示例: del name 解释: 删除name字段
分布式锁
假设现在两台机器一共8个线程都在争抢锁
加锁
那么他们怎么才能算抢到锁呢?
redis发挥作用了,set加上NX参数能保证只有一次set操作能成功,为什么呢?我们暂且可以将redis理解为单线程,所以进来的8个操作是排好队的,听大佬们说好像redis并不完全都是单线程,这儿等我了解后再记录吧。(小本本记上)
set lock 只有自己知道的一个值,可以是随机数 EX 5 NX
既然只有一个操作能成功的话,那不就不用担心资源争抢的问题了,不就实现了分布式加锁了吗?
网上很多方案是用setnx加expire,其实这儿会有一点问题,因为他不是原子性的,如果在setnx之后刚好宕机了,那么这个锁就没有失效时间了,造成死锁,当然,这儿可以用lua脚本操作,lua脚本是具备原子性的
解锁
解锁直接使用del将lock这个键删除不就解锁了吗,不过要注意的是,我们需要判断一下这个锁是不是自己的,怎么判度就是去对比value,这个value只有自己知道,如果一样就删除,那么需要先判断一下,那么又出现了原子问题,这儿我们可以使用lua脚本实现,因为之前说过lua脚本是具备原子性的
if redis.call('get',KEYS[1]) == ARGV[1] then
return redis.call('del',KEYS[1])
else
return 0
end