文章目录

  • 前言
  • 锁细粒度化改造的好处
  • 分级锁的设计和实现
  • 引用

前言


在分布式系统中,想必我们经常会看到锁的应用来保证操作的原子性,使用较简单的例如对象锁,单一锁等等,再高级一点的例如读写锁等等。但是不论是单一锁或者读写锁,在使用上都具有一定的互斥性。这里的互斥性指的是当某个锁持有者持有当前的锁之后,其它线程必须进行阻塞等待操作。这种行为在具有很高workload的系统中,代价还是比较高的。从更深层次来看待这种锁,它是一种单一粒度,较为粗粒度的锁设计模式。那么在实际的应用中,我们是否能够将这种单一锁进行优化呢,使之用起来能够更为的高效。本文笔者将要讲述的将是粗粒度锁的细粒度化改造,改造的实现方式为分级锁的实现。

锁细粒度化改造的好处


为什么我们这么强调锁的细粒度化改造呢?相比于粗粒度锁,它在使用上能够带给系统怎样的帮助呢?

说到这里我们不得不谈到粗粒度锁在使用上的一些弊端,典型的一点比如它会阻塞一些毫无关联的请求操作的处理。比如某个存储系统在根目录下有A,B两个目录,为了保证系统处理请求操作的原子性,我们用锁来做其中的控制。如果我用1个锁来做,则会有一下两种情况发生:

  • 系统在执行A目录下的请求操作,持有锁状态,B目录的所有操作被block住。
  • 系统在执行B目录下的请求操作,持有锁状态,A目录的所有操作被block住。

但其实上面的场景系统在持有锁的情况去保护A目录的并发修改却同样block住了B目录的操作,这其实是可以避免的,我们完全可以让这2个目录的相关操作并发地执行,然后再用2个对应锁去保证这2个目录空间下的请求操作。这样的话,系统的请求吞吐量将会上升很多。

在上面的例子中从一个全局单一锁到2个命名空间目录单独锁的拆分,就是锁细粒化改造的一个简单例子。下面本文将要讲述的分级锁的设计部分也是采用了上述的思路,但是额外多了部分的优化改造,使之更适用于实际系统的使用。

分级锁的设计和实现


本节将要介绍的分级锁的主要特点在于它包含有多个层级的锁,在这里我们以两级锁为例,在此锁内,包含有2个级别锁:

  • Top锁
  • Child锁

在分布锁中,遵守以下规则:

在操作开始前,必须先申请得到Top锁来准备获取Child锁,在获取得到Child锁之后,可以再释放Top锁。这里的Child锁,可以理解为就是每个分区锁。这里Top锁的目的是为了保证获取各个分区锁的原子性。

分级锁原型定义如下:

 /*** LatchLock controls two hierarchical Read/Write locks:* the topLock and the childLock.* Typically an operation starts with the topLock already acquired.* To acquire child lock LatchLock will* first acquire the childLock, and then release the topLock.*/
public abstract class LatchLock<C> {// Interfaces methods to be defined for subclasses/** @return true topLock is locked for read by any thread */protected abstract boolean isReadTopLocked();/** @return true topLock is locked for write by any thread */protected abstract boolean isWriteTopLocked();protected abstract void readTopdUnlock();protected abstract void writeTopUnlock();protected abstract boolean hasReadChildLock();protected abstract void readChildLock();protected abstract void readChildUnlock();protected abstract boolean hasWriteChildLock();protected abstract void writeChildLock();protected abstract void writeChildUnlock();protected abstract LatchLock<C> clone();// Public APIs to use with the classpublic void readLock() {// 在获取child锁后,可以释放top锁readChildLock();readTopdUnlock();}public void readUnlock() {readChildUnlock();}public void writeLock() {// 在获取child锁后,可以释放top锁writeChildLock();writeTopUnlock();}public void writeUnlock() {writeChildUnlock();}
}

在分级锁中,尽管Top锁会是同一个,但是假设我们获取的不同的Child锁,其实不会收到Top锁其它线程持有的情况。因为其它Child锁被lock之后,Top锁就释放了,这样的话其它分级锁的Child锁的获取就不会受到影响了。
在这里Top锁扮演的还是之前全局同一锁的角色,但是所锁住的对象是每个分区的实例而不是每一个具体的操作了。

这里我们以典型的HDFS FSN全局单一锁为例作为Top锁的分级锁实现:

 public class INodeMapLock extends LatchLock<ReentrantReadWriteLock> {Logger LOG = LoggerFactory.getLogger(INodeMapLock.class);private ReentrantReadWriteLock childLock;INodeMapLock() {this(null);}private INodeMapLock(ReentrantReadWriteLock childLock) {assert namesystem != null : "namesystem is null";this.childLock = childLock;}@Overrideprotected boolean isReadTopLocked() {return namesystem.getFSLock().isReadLocked();}@Overrideprotected boolean isWriteTopLocked() {return namesystem.getFSLock().isWriteLocked();}@Overrideprotected void readTopdUnlock() {namesystem.getFSLock().readUnlock("INodeMap", false);}@Overrideprotected void writeTopUnlock() {namesystem.getFSLock().writeUnlock("INodeMap", false, false);}@Overrideprotected boolean hasReadChildLock() {return this.childLock.getReadHoldCount() > 0 || hasWriteChildLock();}@Overrideprotected void readChildLock() {// LOG.info("readChildLock: thread = {}, {}", Thread.currentThread().getId(), Thread.currentThread().getName());this.childLock.readLock().lock();namesystem.getFSLock().addChildLock(this);// LOG.info("readChildLock: done");}@Overrideprotected void readChildUnlock() {// LOG.info("readChildUnlock: thread = {}, {}", Thread.currentThread().getId(), Thread.currentThread().getName());this.childLock.readLock().unlock();// LOG.info("readChildUnlock: done");}@Overrideprotected boolean hasWriteChildLock() {return this.childLock.isWriteLockedByCurrentThread();}@Overrideprotected void writeChildLock() {// LOG.info("writeChildLock: thread = {}, {}", Thread.currentThread().getId(), Thread.currentThread().getName());this.childLock.writeLock().lock();namesystem.getFSLock().addChildLock(this);// LOG.info("writeChildLock: done");}@Overrideprotected void writeChildUnlock() {// LOG.info("writeChildUnlock: thread = {}, {}", Thread.currentThread().getId(), Thread.currentThread().getName());this.childLock.writeLock().unlock();// LOG.info("writeChildUnlock: done");}@Overrideprotected LatchLock<ReentrantReadWriteLock> clone() {return new INodeMapLock(new ReentrantReadWriteLock(false)); // not fair}}

在使用分级锁时,如果遇到可能需要获取多分区(Child)锁时,则要进行多个分区Child锁的获取,之后再释放Top锁,操作方法如下:

  /*** 获取多Child锁,keys为write操作涉及到的相关分区实例*/ public void latchWriteLock(K[] keys) {LatchLock<?> pLock = null;for(K key : keys) {pLock = getPartition(key).partLock;pLock.writeChildLock();}assert pLock != null : "pLock is null";pLock.writeTopUnlock();}

在上面的例子中,遵循的规则如下:

每个partition对应一个partition锁(就是本文提到的分级锁),每个partition锁包含Child锁和Top锁,Top锁是所有partition锁共用的一个锁,Child锁则是每个Partition独有的。所以我们可看到,分级锁在多partition情况下可以很好地得到运用。

本文阐述的分级锁的设计以及实现参考了目前Hadoop社区基于metadata partition的NN改造相关设计,感兴趣的同学可前往引用链接处继续学习了解。

引用


[1].https://issues.apache.org/jira/browse/HDFS-14703 . NameNode Fine-Grained Locking via Metadata Partitioning

细粒度锁的实现之分级锁的设计实现相关推荐

  1. MySQL 锁的相关知识 | lock与latch、锁的类型、简谈MVCC、锁算法、死锁、锁升级

    文章目录 lock与latch 锁的类型 MVCC 一致性非锁定读(快照读) 一致性锁定读(当前读) 锁算法 死锁 锁升级 lock与latch 在了解数据库锁之前,首先就要区分开 lock 和 la ...

  2. JUC-9.“锁”事(显式锁与隐式锁/悲观锁与乐观锁/公平锁与非公平锁/可重入锁/读写锁(独占/共享/降级)/邮戳锁/死锁)、锁升级

    目录 一.悲观锁与乐观锁 1.1 悲观锁 1.2 乐观锁 二.公平锁与非公平锁 2.1 为什么会有公平锁/非公平锁的设计为什么默认非公平? 2.2 如何选择使用哪种锁? 三.可重入锁(又名递归锁) 3 ...

  3. oracle查看dml锁表,【lock】我所认识的Oracle中的锁,DML锁、DDL锁、闩和内部锁。...

    这个问题很简单,你一定对锁有一个认识,但你未必对锁有一个明确的定义. 官方文档这样描述:Locks are mechanisms that prevent destructive interactio ...

  4. mysql innodb 的锁机制_Mysql之Innodb锁机制详解

    InnoDB与MyISAM的最大不同有两点:一是支持事务(TRANSACTION):二是采用了行级锁.关于事务我们之前有专题介绍,这里就着重介绍下它的锁机制. 总的来说,InnoDB按照不同的分类共有 ...

  5. 可重入锁和不可重入锁详解

    目录 概念: 通俗理解: 可重入锁的工作原理: ReenTrantLock可重入锁和synchronized的区别: ReentrantLock源码分析: 可重入锁代码演示: 概念: Reentran ...

  6. java锁(公平锁和非公平锁、可重入锁(又名递归锁)、自旋锁、独占锁(写)/共享锁(读)/互斥锁、读写锁)

    前言 本文对Java的一些锁的概念和实现做个整理,涉及:公平锁和非公平锁.可重入锁(又名递归锁).自旋锁.独占锁(写)/共享锁(读)/互斥锁.读写锁 公平锁和非公平锁 概念 公平锁是指多个线程按照申请 ...

  7. java ibatis 锁表_oracle查看被锁的表和解锁

    https://www.cnblogs.com/XQiu/p/5212787.html --以下几个为相关表 SELECT * FROM v$lock; SELECT * FROM v$sqlarea ...

  8. java查看对象锁级别_对象级别锁 vs 类级别锁(Java)

    前言 对于多线程(并发)和Spring Boot这两块在同步进行学习中,在看到使用synchronized关键字使操作同步时,看到和C#中不一样的东西,所以这里呢,就深入学习了下,若有错误之处,还望指 ...

  9. mysql 锁语句_mysql-笔记 事务 锁 语句

    Start Transaction 或 begin [work] 开始一个事务,开始一个事务,引起其他未提交的事务提交,引起表锁释放 commit 提交事务,永久修改 rollback 回滚事务,撤消 ...

  10. Java Synchronized获得类的锁和获得对象的锁有什么区别呢?

    对象锁&类锁 对象锁 当一个对象中有synchronized method或synchronized block的时候调用此对象的同步方法或进入其同步区域时,就必须先获得对象锁.如果此对象的对 ...

最新文章

  1. mysql自动备份与还原
  2. python下载教程win10-win10系统下如何安装Python软件
  3. 令人郁闷的discuz!个人空间过滤机制
  4. php上传文件自动删除,jsp-解决文件上传后重启Tomcat时文件自动删除问题
  5. spring boot 入门_SpringBoot入门建站全系列(三十)Mybatis多数据源进行数据库操作
  6. 使用sikuli和Arquillian测试HTML5 canvas应用程序
  7. Redis基础1(定义及基础)
  8. java solr5.2_solr5.2.1-----环境搭建
  9. Python小屋刷题软件2425道题目分类速查表
  10. GPS、RTK、PPK三种定位技术的原理及应用
  11. 【Delphi】如何在三轴加速器的频谱分析中使用FFT(快速傅里叶变换)算法
  12. 三菱 J2 J2S J3 J4 编码器 电机文件 修改ID 修改功率 修改型号
  13. win10 外接usb摄像头_win10系统外接usb摄像头怎么打开
  14. 如何注册微信小程序的开发账号
  15. PS1545L-ASEMI低压降肖特基二极管PS1545L
  16. 网页加速之Chromium 预加载 Prerendering
  17. linux内top命令,Linux中的top命令的详细解释
  18. 2022全网最全Java面试题-小米社招面试经验java,面试题整理(一面二面)
  19. 【Unity】Firebase-Google登录身份验证功能接入流程
  20. FishC笔记—19 讲 函数:我的地盘听我的

热门文章

  1. linux中打开caj文件,Ubuntu20.04使用CAJViewer for Linux(可双击打开.caj文件)
  2. android上查看浏览器内核版本号,各种浏览器的userAgent及如何使用JS来检测游览器类型,或android是什么版本号...
  3. matlab 输入Angstrom (埃,埃米,Angstrom 或ANG或Å)
  4. 长亭科技安服实习面试
  5. 硬盘运行与“AHCI 模式”还是“IDE 模式”
  6. 荣耀手机总显示无法连接服务器,荣耀继承者无法连接服务器是什么原因
  7. 案例|工业物联网解决方案•生产数据可视化
  8. 20.数据集成、数据整合、数据融合
  9. 计算机存储单位字节(Byte)以及单位之间的转换
  10. 阿米洛键盘取消win_阿米洛键盘驱动|阿米洛樱花键盘驱动下载v1.0官方版 - 欧普软件下载...