分布式锁及实现思路

80 阅读4分钟

我正在参加「掘金·启航计划」

1. 什么是分布式锁

分布式锁,即分布式系统中的锁。在单体应用中我们通过锁解决的是控制共享资源访问的问题,而分布式锁,就是解决了分布式系统中控制共享资源访问的问题。与单体应用不同的是,分布式系统中竞争共享资源的最小粒度从线程升级成了进程。

2. 分布式锁的由来

2.1 使用场景

超卖,抢单等

单体应用中的锁,多个请求竞争锁的时候,当一个请求获取到锁以后,其他请求就要处于等待的状态。锁是共享在单体应用中的。而多个应用中各个应用的锁是不能共享的,所以就会出现超卖现象。

2.2 超卖原因

image-20221027203237880.png 多个请求,发送到单体应用时,单体锁就可以将资源锁定。不会出现超卖。

image-20221027204125943.png 多个请求,发送的Ngix 转发到多个服务器时,单体锁不能共享,所以就会出现超卖现象。

3.实现思路

如上图,需要一个锁放在多个服务器可以共享的位置,并且可以进行统一管理。需要增加一个“中间管理者”。中间管理需要具有唯一性存储和删除。可以用Mysql的主键和唯一索引来实现,可以用Redis K、V对存储实现,可以用Zookeeper 的节点来实现

4.解决方案

4.1 Mysql解决方案

实施方案:在Mysql 中建一张表, 可以用主键或者唯一索引来控制,当获取到分布式锁时,在表中插入一条记录,业务处理完成后,再将次记录进行删除。 缺点:对此对Mysql数据库进行读写,性能太差,一般不使用。

4.2 Redis解决方案

实施方案:通过使用KV对储存,K - 分布式锁设置唯一值 ,V - 获取锁的自身标识及相关其他需存储的信息。通过setnx 及设置过期时间命令实现。 问题:1、Redis 单体使用,宕机后会出现超卖。2、执行时间大于过期时间。3、删除了其他请求的锁 解决方法:问题1、使用集群且redis 为奇数台,并且多台redis 都要存储,当设置成功的台数超过半数,即为设置成功。如果有redis宕机,不要立即启动, 需要等待宕机的redis中设计的相关的锁,全部过期后,再重启。(Redisson 红锁原理)问题2、设置watch dog 对锁进行延时操作,通过Key 获取对应的 Value, 如果value存在并且是此次请求对应的未完成标识,在重新对key 设置过期时间。直到请求执行得到结果。问题3,通过通过Key 获取对应的 Value,判断value是否包含自己锁的标识,是则删除,否则不删除。

4.3 Zookeeper解决方案

实施方案:用zookeeper临时有序节点实现。请求加锁时,在zookeeper对应的指定节点的目录下,生成一个唯一的瞬时有序节点。 判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。 当释放锁的时候,只需将这个瞬时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。 (第三方库有 Curator,Curator提供的InterProcessMutex是分布式锁的实现)

缺点:

(1)性能上可能并没有缓存服务那么高,因为每次在创建锁和释放锁的过程中,都要动态创建、销毁瞬时节点来实现锁功能。ZK中创建和删除节点只能通过Leader服务器来执行,然后将数据同步到所有的Follower机器上。 (2)zookeeper的并发安全问题:因为可能存在网络抖动,客户端和ZK集群的session连接断了,zk集群以为客户端挂了,就会删除临时节点,这时候其他客户端就可以获取到分布式锁了。