首页技术文章正文

分布式锁是什么?有什么作用?

更新时间:2020-10-30 来源:黑马程序员 浏览量:

分布式锁是控制分布式系统之间同步访问共享资源的一种方式。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要通过一些互斥手段来防止彼此之间的干扰,以保证一致性,在这种情况下,就需要使用分布式锁了。

在平时的实际项目开发中,我们往往很少会去在意分布式锁,而是依赖于关系型数据库固有的排他性来实现不同进程之间的互斥。这确实是一种非常简便且被广泛使用的分布式锁实现方式。然而有一个不争的事实是,目前绝大多数大型分布式系统的性能瓶颈都集中在数据库操作上。因此,如果上层业务再给数据库添加一些额外的锁,例如行锁、表锁甚至是繁重的事务处理,那么就会让数据库更加不堪重负。

下面我们来看看使用ZooKeeper如何实现分布式锁,这里主要讲解排他锁共享锁两类分布式锁。

排他锁

排他锁(Exclusive Locks,简称 X 锁),又称为写锁或独占锁,是一种基本的锁类型。如果事务 T1对数据对象 O1加上了排他锁,那么在整个加锁期间,只允许事务 T1对 O1进行读取和更新操作,其他任何事务都不能再对这个数据对象进行任何类型的操作——直到T1释放了排他锁

从上面讲解的排他锁的基本概念中,我们可以看到,排他锁的核心是如何保证当前有且仅有一个事务获得锁,并且锁被释放后,所有正在等待获取锁的事务都能够被通知到。

下面我们就来看看如何借助ZooKeeper实现排他锁:

① 定义锁

在通常的Java开发编程中,有两种常见的方式可以用来定义锁,分别是synchronized机制和JDK5提供的ReentrantLock。然而,在ZooKeeper中,没有类似于这样的API可以直接使用,而是通过 ZooKeeper 上的数据节点来表示一个锁,例如/exclusive_lock/lock节点就可以被定义为一个锁,如图:

1604046911092_分布式锁01.jpg

② 获取锁

在需要获取排他锁时,所有的客户端都会试图通过调用 create()接口,在/exclusive_lock节点下创建临时子节点/exclusive_lock/lock。在前面,我们也介绍了,ZooKeeper 会保证在所有的客户端中,最终只有一个客户端能够创建成功,那么就可以认为该客户端获取了锁。同时,所有没有获取到锁的客户端就需要到/exclusive_lock 节点上注册一个子节点变更的Watcher监听,以便实时监听到lock节点的变更情况

③释放锁

在“定义锁”部分,我们已经提到,/exclusive_lock/lock 是一个临时节点,因此在以下两种情况下,都有可能释放锁。 · 当前获取锁的客户端机器发生宕机,那么ZooKeeper上的这个临时节点就会被移除。 · 正常执行完业务逻辑后,客户端就会主动将自己创建的临时节点删除。 无论在什么情况下移除了lock节点,ZooKeeper都会通知所有在/exclusive_lock节点上注册了子节点变更Watcher监听的客户端。这些客户端在接收到通知后,再次重新发起分布式锁获取,即重复“获取锁”过程。整个排他锁的获取和释放流程,如下图:

1604046922535_分布式锁02.jpg


共享锁

共享锁(Shared Locks,简称S锁),又称为读锁,同样是一种基本的锁类型。

如果事务T1对数据对象O1加上了共享锁,那么当前事务只能对O1进行读取操作,其他事务也只能对这个数据对象加共享锁——直到该数据对象上的所有共享锁都被释放。

共享锁和排他锁最根本的区别在于,加上排他锁后,数据对象只对一个事务可见,而加上共享锁后,数据对所有事务都可见。

下面我们就来看看如何借助ZooKeeper来实现共享锁。

① 定义锁
和排他锁一样,同样是通过 ZooKeeper 上的数据节点来表示一个锁,是一个类似于“/shared_lock/[Hostname]-请求类型-序号”的临时顺序节点,例如/shared_lock/host1-R-0000000001,那么,这个节点就代表了一个共享锁,如图所示:

1604046935038_分布式锁03.jpg

② 获取锁

在需要获取共享锁时,所有客户端都会到/shared_lock 这个节点下面创建一个临时顺序节点,如果当前是读请求,那么就创建例如/shared_lock/host1-R-0000000001的节点;如果是写请求,那么就创建例如/shared_lock/host2-W-0000000002的节点。

判断读写顺序

通过Zookeeper来确定分布式读写顺序,大致分为四步

(1)创建完节点后,获取/shared_lock节点下所有子节点,并对该节点变更注册监听。

(2)确定自己的节点序号在所有子节点中的顺序。

(3)对于读请求:若没有比自己序号小的子节点或所有比自己序号小的子节点都是读请求,那么表明自己已经成功获取到共享锁,同时开始执行读取逻辑,若有写请求,则需要等待。对于写请求:若自己不是序号最小的子节点,那么需要等待。

(4)接收到Watcher通知后,重复步骤1

③ 释放锁,其释放锁的流程与独占锁一致。


猜你喜欢:

HBase分布式数据库的特点是什么?HBase简介 

Java分布式事务处理视频教程 





分享到:
在线咨询 我要报名
和我们在线交谈!