分布式锁的实现方案
(1)用数据库实现分布式锁比较简单,就是创建一张锁表,数据库对字段作唯一性约束。加锁的时候,在锁表中增加一条记录即可;释放锁的时候删除锁记录就行。如果有并发请求同时提交到数据库,数据库会保证只有一个请求能够得到锁。这种属于数据库IO操作,效率不高,而且频繁操作会增大数据库的开销,因此这种方式在高并发、高性能的场景中用的不多。
(2)基于redis分布式锁:
①理论上来说使用缓存来实现分布式锁的效率最高,加锁速度最快,因为Redis几乎都是纯内存操作,而基于数据库的方案和基于Zookeeper的方案都会涉及到磁盘文件IO,效率相对低下。
②redis提供了SETNX命令去实现锁的排他性,当key不存在返回1,存在返回0,还可以使用expire命令去设置锁的失效时间从而避免死锁的问题。对于加锁与设置过期时间是非原子操作,我们可以使用Lua脚本。Redisson框架提供了一个分布式锁的封装实现,并且内置了一个叫看门狗Watch Dog的机制,来对加锁成功后还想继续持有锁的进行key的续期。
③如果线程1在Redis的master节点上拿到了锁,但是加锁的key还没同步到slave节点。恰好这时,master节点发生了故障,一个slave节点就会升级为master节点。线程2就可以获取到这个key的锁了,但是线程1已经拿到锁了,锁的安全性就没有了,可以使用RedLock。
(3)基于Zookeeper:Zookeeper利用临时有序节点实现分布式锁。(缺点:客户端在持有锁期间,需要定期向Zookeeper发送心跳,以保持锁的状态。如果客户端因为异常退出或网络故障等原因无法发送心跳,Zookeeper会认为客户端已经释放了锁。)
在zookeeper中建一个分布式锁的节点。
步骤1:客户端A在锁的节点创建一个临时有序节点001
步骤2:看001是不是第一个节点,看序号有没有比它小的,是第一个节点就获取到锁。
步骤3:客户端B创建临时有序节点002
步骤4:判断002是否是第一个节点,不是第一个节点则给上一个节点用watch加监听器。
步骤5:客户端A执行完业务逻辑后,需要释放锁了,删除临时有序节点
步骤5:等到第一个节点释放锁,删除了节点后就会被002监听到。
步骤6:zookeeper通知客户端B第一个节点被删除了。
步骤8:此时客户端B就会再次判断自己是不是第一个节点
步骤9:是的话就会加锁成功
补充:
1.1 zookeeper节点分类:
①临时节点:与客户端断开连接后删除
a.临时目录节点:节点名称不编号
b.临时有序节点:节点名称进行顺序编号
②持久节点:与客户端断开连接后不删除
a.持久目录节点:节点名称不编号
b.持久有序节点:节点名称进行顺序编号
1.2 临时有序节点可以通过watch命令监听到节点的增删改