Article Outline
分布式锁一般有以下几种实现方式:
数据库乐观锁
利用主键唯一的特性,如果有多个请求同时提交到数据库的话,数据库会保证只有一个操作可以成功,那么我们就可以认为操作成功的那个线程获得了该方法的锁,当方法执行完毕之后,想要释放锁的话,删除这条数据库记录即可。
Memcached分布式锁
利用Memcached的add命令。此命令是原子性操作,只有在key不存在的情况下,才能add成功,也就意味着线程得到了锁。
基于redis的分布式锁
和Memcached的方式类似,利用Redis的setnx命令。此命令同样是原子性操作,只有在key不存在的情况下,才能set成功。(setnx命令并不完善,后续会介绍替代方案)
基于zookeeper的分布式锁
- 在zookeeper指定节点(locks)下创建临时顺序节点node_n
- 获取locks下所有子节点children
- 对子节点按节点自增序号从小到大排序
- 判断本节点是不是第一个子节点,若是,则获取锁;若不是,则监听比该节点小的那个节点的删除事件
- 若监听事件生效,则回到第二步重新进行判断,直到获取到锁
Chubby
锁服务短暂失效时(服务器宕机),Chubby需要保持所有锁的持有状态,以避免持有锁的客户端出现问题.而细粒度锁通常设计为,锁服务一旦失效就释放所有锁
至少确保锁的实现同时满足以下四个条件:
- 互斥:在任意时刻,只有一个客户端能持有锁
- 无死锁:不会发生死锁.即有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁(异常流程强制解锁)
- 容错:具有容错性.只要大部分的redis节点正常运行,客户端就可以加锁和解锁
- 解铃换需系铃人.加锁和解锁必须是同一个客户端,指定客户端自己不能解除其他客户端加的锁
举例:
String result = jedis.set(key,value,"NX","PX",expireTime)
一共有五个形参
key,我们用key来当锁,因为key是唯一的 value,可以使用uuid来确定这个把锁是某个请求单独持有的,确保解锁时也是唯一,具备可靠性 nxxx,填NX,代表SET IF NOT EXIST ,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作 expx,传PX,代表要给这个key加一个过期的设置,具体参数由第五个参数决定 time,与第四个参数相互呼应,代表key的过期时间