说起redis中的锁,可能就
- Setnx
- RedLock
- Redission
Setnx
setnx命令并非单单指redis的setnx key value这条命令
一般代指redis中对set命令加上NX参数进行使用,set命令的参数有
[EX seconds|PX milliseconds] [NX|XX] [KEEPTTL]
setnx的大致原理,主要是key不存在才能set成功,一个进程拿到锁,在没有del 锁之前,别的进程获取锁就会失败
另外我们最好设置一个EX或者PX参数,表示超时时间,获得锁的那个进程崩了而且锁没有释放;
然而就算是这样,也不能保证万无一失,如果有一个进程A操作锁内的资源超过了超时时间,那么其他进程就会拿到锁,等进程A回来了,反手把锁删除了怎么办,这个锁按理说需要其他进程释放才算正常,造成的结果就是其他进程要释放锁的时候,找不到锁了?其实这还算好的,万一又有另一个进程过来加锁,这时候加锁是会成功的,因为这个时候锁被A释放了,在B释放锁之前,C进程又来加锁,且成功了,然后B把C加的锁释放了。。。这样下去不是GG?
这时候我们就会想到为什么不给每个锁加上一个身份标识呢,防止不同的进程来释放我的锁。于是我们就会setnx key value中value不能闲着,value可以设置一个唯一的客户端ID或者UUID(通过雪花算法实现 链接)这种随机数
这样的话,当我们解锁的时候,先获取value判断是否是当前进程加的锁,再去删除锁
伪代码:
string uuid = xxxxx
set test uuid NX PX 3000
try:
...
finally:
if uuid == redisTool.get('test'):
redisTool.del('test')
我们会发现,get和del不是原子性的操作,这就会有进程安全问题
但是小公司的话,并发程度可能并不是那么高,那么上面这种类型的代码还是可以对付的,如果是高并发的话,那就需要用Lua脚本
具体实现原理是什么呢,其实就是一个脚本对应是一个eval/evalsha命令,你脚本内容再多,也就是这个命令执行而已
其实解决上面这种问题除了用Lua脚本之外,还可以用很出名的
Redission
现在的redis已经到了5.0版本了,但是在2.6之前,set是不能加nx参数的,也就没有setnx这一说法
以前的setnx需要两步(现在setnx代表的是set命令加上nx参数)
setnx test uuid
expire test 30
设置key和设置有效期是分成两步的,这就会有不保证原子性的问题
Redission是Java的redis客户端之一,我对Java研究不深,所以只是了解一下它的锁机制实现的原理
redission普通的锁主要是RedissioinLock这个类,加锁,释放锁操作都是通过Lua脚本实现的,虽说setnx就已经能够实现加锁释放锁的安全性,但是redission的Lua脚本更加全面
RedLock
红锁
这是redis官方提出的一种分布式锁的算法
RedLock算法虽然是需要多个实例,但是这些实例都是独自部署的,没有主从关系
之所以要独立,是为了避免redis异步复制造成的锁丢失,例如:主节点没来得及把刚刚set进来的数据发给从节点,然后主节点就挂了,这拼的就是高可用性啊
RedLock还没有理解到位