文档章节

Hbase WAL线程模型源码分析

偶素浅小浅
 偶素浅小浅
发布于 2016/12/11 21:58
字数 1864
阅读 21
收藏 0

版权声明:本文由熊训德原创文章,转载请注明出处: 
文章原文链接:https://www.qcloud.com/community/article/257

来源:腾云阁 https://www.qcloud.com/community

 

Hbase的WAL机制是保证hbase使用lsm树存储模型把随机写转化成顺序写,并从内存read数据,从而提高大规模读写效率的关键一环。wal的多生产者单消费者的线程模型让wal的写入变得安全而高效。

在文章《WAL在RegionServer调用过程》中从代码层面阐述了一个client的“写”操作是如何到达Hbase的RegionServer,又是如何真正地写入到wal(FSHLog)文件,再写入到memstore。但是hbase是支持mvcc机制的存储系统,本文档将说明RegionServer是如何把多个客户端的“写”操作安全有序地落地日志文件,又如何让client端优雅地感知到已经真正的落地。

wal为了高效安全有序的写入,笔者认为最关键的两个机制是wal中使用的线程模型和多生产者单消费者模型。

线程模型

其线程模型主要实现实在FSHLog中,FSHLog是WAL接口的实现类,实现了最关键的apend()和sync()方法,其模型如图所示:

这个图主要描述了HRegion中调用append和sync后,hbase的wal线程流转模型。最左边是有多个client提交到HRegion的append和sync操作。当调用append后WALEdit和WALKey会被封装成FSWALEntry类进而再封装成RinbBufferTruck类放入一个线程安全的Buffer(LMAX Disruptor RingBuffer)中。当调用sync后会生成一个SyncFuture进而封装成RinbBufferTruck类同样放入这个Buffer中,然后工作线程此时会被阻塞等待被notify()唤醒。在最右边会有一个且只有一个线程专门去处理这些RinbBufferTruck,如果是FSWALEntry则写入hadoop sequence文件。因为文件缓存的存在,这时候很可能client数据并没有落盘。所以进一步如果是SyncFuture会被批量的放到一个线程池中,异步的批量去刷盘,刷盘成功后唤醒工作线程完成wal。

源码分析

下面将从源码角度分析其中具体实现过程和细节。

工作线程中当HRegion准备好一个行事务“写”操作的,WALEdit,WALKey后就会调用FSHLog的append方法:

FSHLog的append方法首先会从LAMX Disruptor RingbBuffer中拿到一个序号作为txid(sequence),然后把WALEdit,WALKey和sequence等构建一个FSALEntry实例entry,并把entry放到ringbuffer中。而entry以truck(RingBufferTruck,ringbuffer实际存储类型)通过sequence和ringbuffer一一对应。

如果client设置的持久化等级是USER_DEFAULT,SYNC_WAL或FSYNC_WAL,那么工作线程的HRegion还将调用FSHLog的sync()方法:



追踪代码可以分析出Sync()方法会往ringbuffer中放入一个SyncFuture对象,并阻塞等待完成(唤醒)。

像模型图中所展示的多个工作线程封装后拿到由ringbuffer生成的sequence后作为生产者放入ringbuffer中。在FSHLog中有一个私有内部类RingBufferEventHandler类实现了LAMX Disruptor的EventHandler接口,也即是实现了OnEvent方法的ringbuffer的消费者。Disruptor通过 java.util.concurrent.ExecutorService 提供的线程来触发 Consumer 的事件处理,可以看到hbase的wal中只启了一个线程,从源码注释中也可以看到RingBufferEventHandler在运行中只有单个线程。由于消费者是按照sequence的顺序刷数据,这样就能保证WAL日志并发写入时只有一个线程在真正的写入日志文件的可感知的全局唯一顺序。

RingBufferEventHandler类的onEvent()(一个回调方法)是具体处理append和sync的方法。在前面说明过wal使用RingBufferTruck来封装WALEntry和SyncFuture(如下图源码),在消费线程的实际执行方法onEvent()中就是被ringbuffer通知一个个的从ringbfer取出RingBufferTruck,如果是WALEntry则使用当前HadoopSequence文件writer写入文件(此时很可能写的是文件缓存),如果是SyncFuture则简单的轮询处理放入SyncRunner线程异步去把文件缓存中数据刷到磁盘。这里再加一个异步操作去真正刷文件缓存的原因wal源码中有解释:刷磁盘是很费时的操作,如果每次都同步的去刷client的回应比较快,但是写效率不高,如果异步刷文件缓存,写效率提高但是友好性降低,在考虑了写吞吐率和对client友好回应平衡后,wal选择了后者,积累了一定量(通过ringbuffer的sequence)的缓存再刷磁盘以此提高写效率和吞吐率。这个决策从hbase存储机制最初采用lsm树把随机写转换成顺序写以提高写吞吐率,可以看出是目标一致的。

这部分源码可以看到RingBufferTruck类的结构,从注释可以看到选择SyncFuture和FSWALEntry一个放入ringbuffer中。

这部分源码可以看到append的最终归属就是根据sequence有序的把FSWALEntry实例entry写入HadoopSequence文件。这里有序的原因是多工作线程写之前通过ringbuffer线程安全的CAS得到一个递增的sequence,ringbuffer会根据sequence取出FSWALEntry并落盘。这样做其实只有在得到递增的sequence的时候需要保证线程安全,而java的CAS通过轮询并不用加锁,所以效率很高。具体有关ringbuffer说明和实现可以参考LMAX Disruptor文档:

https://github.com/LMAX-Exchange/disruptor/wiki/Introduction.

这部分源码是说明sync操作的SyncFuture会被提交到SyncRunner中,这里可以注意SyncFuture实例其实并不是一个个提交到SyncRunner中执行的,而是以syncFutures(数组,多个SyncFuture实例)方式提交的。下面这部分源码是注释中说明批量刷盘的决策。

SyncRunner是一个线程,wal实际有一个SyncRunner的线程组,专门负责之前append到文件缓存的刷盘工作。

SyncRunner的线程方法(run())负责具体的刷写文件缓存到磁盘的工作。首先去之前提交的synceFutues中拿到其中sequence最大的SyncFuture实例,并拿到它对应ringbuffer的sequence。再去比对当前最大的sequence,如果发现比当前最大的sequence则去调用releaseSyncFuture()方法释放synceFuture,实际就是notify通知正被阻塞的sync操作,让工作线程可以继续往下继续。前面解释了sequence是根据提交顺序过来的,并且解释了append到文件缓存的时候也是全局有序的,所以这里取最大的去刷盘,只要最大sequence已经刷盘,那么比这个sequence的也就已经刷盘成功。最后调用当前HadoopSequence文件writer刷盘,并notify对应的syncFuture。这样整个wal写入也完成了。

小结

Hbase的WAL机制是保证hbase使用lsm树存储模型把随机写转化成顺序写,并从内存read数据,从而提高大规模读写效率的关键一环。wal的多生产者单消费者的线程模型让wal的写入变得安全而高效,本文档从源码入手分析了其线程模型为以后更好开发和研究hbase其他相关知识奠定基础。

本文转载自:

偶素浅小浅
粉丝 8
博文 202
码字总数 0
作品 0
信阳
私信 提问
HBase - 数据写入流程解析

本文由 网易云 发布 作者:范欣欣 本篇文章仅限内部分享,如需转载,请联系网易获取授权。 众所周知,HBase默认适用于写多读少的应用,正是依赖于它相当出色的写入性能:一个100台RS的集群可...

wangyiyungw
2018/05/10
0
0
HBase在阿里搜索中的应用实践

李钰,花名绝顶,WOTA全球架构与运维技术峰会分享嘉宾,现任阿里巴巴搜索事业部高级技术专家,HBase开源社区PMC & committer。开源技术爱好者,主要关注分布式系统设计、大数据基础平台建设等...

雪夜凋零
2018/06/26
0
0
HBase原理之HBase MetaStore&Compaction剖析

1.概述 客户端读写数据是先从HBase Clienr获取RegionServer的元数据信息,比如Region地址信息。在执行数据写操作时,HBase会先写MetaStore,为什么会写到MetaStore。本篇文章将为读者剖析HBa...

HBase技术社区
2018/09/23
0
0
Hbase原理以及基本运行方式和优化

HBase是一个构建在HDFS上的分布式列存储系统; HBase是基于Google BigTable模型开发的,典型的key/value系统; HBase是Apache Hadoop生态系统中的重要一员,主要用于海量非结构化数据存储; ...

脸大的都是胖纸
2015/07/06
1K
0
HBase Replication详解

Replication:复制,指的是持续的将同一份数据拷贝到多个地方进行存储,是各种存储系统中常见而又重要的一个概念,可以指数据库中主库和从库的复制,也可以指分布式集群中多个集群之间的复制...

浮躁的码农
09/29
10
0

没有更多内容

加载失败,请刷新页面

加载更多

学习记录 java面试题(一)

1. JDK和JRE的区别 JDK是整个JAVA的核心,包括了Java运行环境JRE,一堆Java工具和Java基础的类库。通过JDK开发人员将源码 文件(java文件)编译成字节码文件(class文件)。 JRE是Java运行环境,...

Pole丶逐
33分钟前
8
0
springboot 部署到外部tomcat

入口类继承SpringBootServletInitializer 并重写protected SpringApplicationBuilder configure(SpringApplicationBuilder builder)方法 如下 import org.springframework.boot.SpringApplic......

雷开你的门
39分钟前
5
0
hashCode和equals方法的关系

equals相等,hashcode必相等; hashCode()在哈希表中起作用,如HashSet、HashMap等。 当我们向哈希表(如HashSet、HashMap等)中添加对象object时,首先调用hashCode()方法计算object的哈希码,...

无名氏的程序员
43分钟前
7
0
技术分享 | MySQL 慢查询记录原理和内容解析

作者:高鹏 文章末尾有他著作的《深入理解 MySQL 主从原理 32 讲》,深入透彻理解 MySQL 主从,GTID 相关技术知识。 源码版本:percona 5.7.14 本文为学习记录,可能有误请谅解,也提供了一些...

爱可生
52分钟前
5
0
elementui 树型节点

节点选择时,勾选节点。 提交给后端时,传递 this.$refs.menuTree.getCheckedKeys(); 半选父节点 getHalfCheckedKeys() 不需要提交。...

东东笔记
52分钟前
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部