1.分布式锁条件
为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:
-
互斥性。在任意时刻,只有一个客户端能持有锁。
-
不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
-
具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。
-
解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
2.分布式锁一般有三种实现方式:
-
数据库乐观锁
-
基于Redis的分布式锁
-
基于Zookeeper的分布式锁
3.数据库乐观锁
数据库乐观锁的实现方式是先使用SELECT语句查询某字段的值(版本号),该字段即理解为要获取的分布式锁。然后在使用 UPDATE语句对正常业务数据进行更新,在UPDATE语句执行时一定要用WHERE条件对版本号进行判断,若版本号在这段时间 内并没有发生变化则该语句默认执行成功,否则循环执行即可。
4.基于Zookeeper的分布式锁
基于Zookeeper实现分布式锁的算法思路大致如下假设锁空间的根节点为/lock:
-
客户端连接zookeeper,并在/lock下创建临时的且有序的子节点,第一个客户端对应的子节点为/lock/lock-0000000000,第二个为/lock/lock-0000000001,以此类推。
-
客户端获取/lock下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁,否则监听/lock的子节点变更消息,获得子节点变更通知后重复此步骤直至获得锁。
-
执行业务代码。
-
完成业务流程后,删除对应的子节点释放锁。
5.基于Redis的分布式锁
需要的命令:
setNx:当key不存在时返回1,当key存在时返回0,我们可以根据这个来判断是否存在锁。
expier:使用这个命令为key设置一个过期时间,超过这个时间锁会自动释放,避免死锁。
实现思想:
1.获取锁的时候,使用setNx加锁,并使用expire命令给这个锁设置一个过期时间,
过期自动释放锁,锁的value值是一个UUID的随机数,在释放锁的时候判断是不是同一个锁。
2.获取锁的时候还要设置一个超时获取时间,如果超过这个时间就放弃获取这个锁。
3.释放锁的时候,根据UUID判断是不是同一个锁,如果是同一个锁,使用delete命令进行锁释放。