分布式锁三大要义
独享 (同一把锁只会被一个线程获取)
不会死锁(超时释放)
容错 节点存活就可以被获取到锁
常用的redis分布式锁实现方案
设置锁
set key value px 3000 nx
释放锁,需要用lua脚本来实现,为什么用lua,因为redis是单线程的,get 和del不是原子操作
if
redis.call(‘get’,KEYS[1] ==ARGV[1])
then
return redis.call(‘del’,KEYS[1])
else
return 0
end
这里三大面试要点
set命令要用set key value px milliseconds nx
value要具有唯一性
释放锁时要验证value值,不能误解锁
缺点
主从切换会导致锁丢失
Redis主节点拿到了锁,还没有同步到从节点 (Master和Slave是异步通讯的, Redis数据同步怎么实现的)
主节点挂了,从节点又被拿到锁,锁丢失了
RedLock算法
在Redis的分布式环境中,我们假设有N个Redis master。这些节点完全互相独立,不存在主从复制或者其他集群协调机制。之前我们已经描述了在Redis单实例下怎么安全地获取和释放锁。我们确保将在每(N)个实例上使用此方法获取和释放锁。在这个样例中,我们假设有5个Redis master节点,这是一个比较合理的设置,所以我们需要在5台机器上面或者5台虚拟机上面运行这些实例,这样保证他们不会同时都宕掉。
为了取到锁,客户端应该执行以下操作:
- 获取当前Unix时间,以毫秒为单位。
- 依次尝试从N个实例,使用相同的key和随机值获取锁。在步骤2,当向Redis设置锁时,客户端应该设置一个网络连接和响应超时时间,这个超时时间应该小于锁的失效时间。例如你的锁自动失效时间为10秒,则超时时间应该在5-50毫秒之间。这样可以避免服务器端Redis已经挂掉的情况下,客户端还在死死地等待响应结果。如果服务器端没有在规定时间内响应,客户端应该尽快尝试另外一个Redis实例。
- 客户端使用当前时间减去开始获取锁时间(步骤1记录的时间)就得到获取锁使用的时间。当且仅当从大多数(这里是3个节点)的Redis节点都取到锁,并且使用的时间小于锁失效时间时,锁才算获取成功。
- 如果取到了锁,key的真正有效时间等于有效时间减去获取锁所使用的时间(步骤3计算的结果)。
- 如果因为某些原因,获取锁失败(没有在至少N/2+1个Redis实例取到锁或者取锁时间已经超过了有效时间),客户端应该在所有的Redis实例上进行解锁(即便某些Redis实例根本就没有加锁成功)。
Java客户端Redisson简要原理
http://www.redis.cn/topics/distlock.html