分布式锁实现
1、zookeeper
实现方式:
方案1:利用节点名称的唯一性来实现共享锁。
算法思路: 利用名称唯一性,加锁操作时,只需要所有客户端一起创建/test/Lock节点,只有一个创建成功,成功者获得锁。解锁时,只需删除/test/Lock节点,其余客户端再次进入竞争创建节点,直到所有客户端都获得锁。
方案2:利用临时顺序节点实现共享锁。(主要是用这种方式实现)
算法思路:对于加锁操作,可以让所有客户端都去/lock目录下创建临时顺序节点,如果创建的客户端发现自身创建节点序列号是/lock/目录下最小的节点,则获得锁。否则,监视比自己创建节点的序列号小的节点(比自己创建的节点小的最大节点),进入等待。比如创建节点:/lock/0000000001、/lock/0000000002、/lock/0000000003。则节点/lock/0000000001会先获得锁,因为zk上的节点是有序的,且都是最小的节点先获得锁。
注:临时顺序节点比持久顺序节点的好处是:当zookeeper宕机后,临时顺序节点会自动删除,获取锁的客户端会释放锁,不会一直造成锁等待,而持久节点会造成锁等待。
两种方式的区别:
- 方案1会产生惊群效应:假如许多客户端在等待一把锁,当锁释放时候所有客户端都被唤醒,然后竞争分布式锁,仅仅有一个客户端得到锁。
- 方案2是按照创建顺序排队的实现,多个客户端共同等待锁,当锁释放时只有一个客户端会被唤醒,在zk上注册节点最小的客户端会被唤醒,避免了惊群效应。
2、redis实现分布式锁
redis实现分布式锁主要靠四个命令:
- setnx(set if not exits 维护着是乐观锁):当不存在key的时候,才为key设置值为value。setnx与set的区别:set是存在key,则去覆盖value;setnx是不存在key,则重新给key和value赋值。
- getset:根据key得到旧的值,并set新的值。
- expire:设置过期时间。
- del:删除
实现方式(文字配合图片可更加清晰):
- 获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。
- 获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。
- 释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。
3、consul、etcd
consul:raft的强一致性、session、创建带锁的KV
etcd:raft的强一致性、lease功能(租约机制)、watch功能
4、数据库实现分布式锁
实现方式:利用的是乐观锁和悲观锁
- 乐观锁:在表中添加版本号的字段,每次更新前都先查询出带版本号的数据,然后再更新的时候where条件语句后带版本号条件,更新成功表示锁已占用,更新不成功表示锁没被占用。
- 悲观锁:利用select...for update(X锁)/select...lock in share mode(S锁),一般来说用X锁的较多,因为后续多会做写功能的实现。
注:当实现悲观锁的时候,需要关闭数据库的事务自动提交机制不然不会生效。
you are the best!