两种实现分布式锁的方法

448 阅读3分钟

为什么需要分布式锁

现在的互联网公司服务都是多机房分布式部署服务,假设你在淘宝买东西,下单的时候重复点了几次,岂不是要多支付几笔(数据库对同一笔订单落库多次),然而实际上不会发生这样的事情,因为服务端做了分布式锁机制来互斥的访问资源,当一次请求抢占了分布式锁,其他请求没有拿到锁就会挂起,不做操作。

16a53749547937bb.png

分布式锁的实现方式

1 基于redis实现
2 基于zk实现
3 自己折腾数据库实现

redis分布式锁实现方式

实现分布式锁要具有原子性,即一些类操作要么全部完成,要么其中一个失败就回滚,可使用
set key value [EX seconds] [PX milliseconds] NX 指令加锁

  • EX seconds: 设定过期时间,单位为秒
  • PX milliseconds: 设定过期时间,单位为毫秒
  • NX: 仅当key不存在时设置值

value要具有唯一性,可以使用跟服务有关的属性来保证,比如订单id这种
释放锁:要判断锁是否是自己加的锁 综上所述: 基于redis实现分布式锁,要三个步骤
第一步获取锁,第二步保证value唯一性,第三步释放锁的时候要判断是否是自己的锁

以上所述在redis集群模式下存在一个风险问题

比如说A客户端在Redis的master节点上拿到了锁,但是这个加锁的key还没有同步到slave节点,master故障,发生故障转移,一个slave节点升级为master节点,B客户端也可以获取同个key的锁,但客户端A也已经拿到锁了,这就导致多个客户端都拿到锁
此时需要说说这个大杀器了,redis的Redlock,假设有3个redis实例,客户端使用相同的key和value获取锁的时候,客户端设置一个网络连接和相应超时时间,这个超时时间应该小于锁的失效时间,假设你的锁失效时间为10秒,超时时间应该远远小于10秒,这样可以避免服务器宕机的情况下,客户端还在等待响应
客户端使用 当前时间- 开始获取锁的时间 = 实际获取锁使用的时间,当超过半数以上(N/2+1)的节点都拿到锁,并且使用时间小于锁失效时间才能获取锁成功。
如果因为某些原因,获取锁失败,客户端要在所有锁上进行解锁(而不是在申请锁的N/2+1)个锁上解锁,是为了防止某些节点获取到锁但是客户端没有得到响应导致接下来一段时间不能被重新获取锁

zk实现分布式锁

可以使用zk创建临时节点,创建成功就获取了这个锁,别的客户端来创建锁就会失败,指南注册一个监听器来监听这个锁。当释放这个锁的时候删除这个节点,一旦释放就会通知客户端,此时其他客户端就可以重新加锁来获取锁。