本文共 4377 字,大约阅读时间需要 14 分钟。
本文转载自 网易范欣欣 (优秀的文章自己保存才放心^-^)
众所周知,HBase默认适用于写多读少的应用,正是依赖于它相当出色的写入性能:一个100台RS的集群可以轻松地支撑每天10T的写入量。当然,为了支持更高吞吐量的写入,HBase还在不断地进行优化和修正,这篇文章结合0.98版本的源码全面地分析HBase的写入流程,全文分为三个部分,第一部分介绍客户端的写入流程,第二部分介绍服务器端的写入流程,最后再重点分析WAL的工作原理。
服务器端RegionServer接收到客户端的写入请求后,首先会反序列化为Put对象,然后执行各种检查操作,比如检查region是否是只读、memstore大小是否超过blockingMemstoreSize等。检查完成之后,就会执行如下核心操作:
WAL(Write-Ahead Logging)是一种高效的日志算法,几乎是所有非内存数据库提升写性能的不二法门,基本原理是在数据写入之前首先顺序写入日志,然后再写入缓存,等到缓存写满之后统一落盘。之所以能够提升写性能,是因为WAL将一次随机写转化为了一次顺序写加一次内存写。提升写性能的同时,WAL可以保证数据的可靠性,即在任何情况下数据不丢失。假如一次写入完成之后发生了宕机,即使所有缓存中的数据丢失,也可以通过恢复日志还原出丢失的数据。
HBase中可以通过设置WAL的持久化等级决定是否开启WAL机制、以及HLog的落盘方式。WAL的持久化等级分为如下四个等级:
用户可以通过客户端设置WAL持久化等级,代码:put.setDurability(Durability. SYNC_WAL );
HBase中,WAL的实现类为HLog,每个Region Server拥有一个HLog日志,所有region的写入都是写到同一个HLog。下图表示同一个Region Server中的3个 region 共享一个HLog。当数据写入时,是将数据对<HLogKey,WALEdit>按照顺序追加到HLog中,以获取最好的写入性能。
上图中HLogKey主要存储了log sequence number,更新时间 write time,region name,表名table name以及cluster ids。其中log sequncece number作为HFile中一个重要的元数据,和HLog的生命周期息息相关,后续章节会详细介绍;region name和table name分别表征该段日志属于哪个region以及哪张表;cluster ids用于将日志复制到集群中其他机器上。WALEdit用来表示一个事务中的更新集合,在之前的版本,如果一个事务中对一行row R中三列c1,c2,c3分别做了修改,那么hlog中会有3个对应的日志片段如下所示:
: : :
然而,这种日志结构无法保证行级事务的原子性,假如刚好更新到c2之后发生宕机,那么就会产生只有部分日志写入成功的现象。为此,hbase将所有对同一行的更新操作都表示为一个记录,如下:
:
其中WALEdit会被序列化为格式
<-1, # of edits,, , >
,比如
<-1, 3,, , >
,其中-1作为标示符表征这种新的日志结构。
了解了HLog的结构之后,我们就开始研究HLog的写入模型。HLog的写入可以分为三个阶段,首先将数据对<HLogKey,WALEdit>写入本地缓存,然后再将本地缓存写入文件系统,最后执行sync操作同步到磁盘。在以前老的写入模型中,上述三步都由工作线程独自完成,如下图所示:
上图中,本地缓存写入文件系统那个步骤工作线程需要持有updateLock执行,不同工作线程之间必然会恶性竞争;不仅如此,在Sync HDFS这步中,工作线程之间需要抢占flushLock,因为Sync操作是一个耗时操作,抢占这个锁会导致写入性能大幅降低。所幸的是,来自中国(准确的来说,是来自小米,鼓掌)的3位工程师意识到了这个问题,进而提出了一种新的写入模型并被官方采纳。根据官方测试,新写入模型的吞吐量比之前提升3倍多,单台RS写入吞吐量介于12150~31520,5台RS组成的集群写入吞吐量介于22000~70000(见HBASE-8755)。下图是小米官方给出来的对比测试结果:
在新写入模型中,本地缓存写入文件系统以及Sync HDFS都交给了新的独立线程完成,并引入一个Notify线程通知工作线程是否已经Sync成功,采用这种机制消除上述锁竞争,具体如下图所示:通过上述过程的梳理可以知道,新写入模型采取了多线程模式独立完成写文件系统、sync磁盘操作,避免了之前多工作线程恶性抢占锁的问题。同时,工作线程在将WALEdit写入本地Buffer之后并没有马上阻塞,而是释放行锁之后阻塞等待WALEdit落盘,这样可以尽可能地避免行锁竞争,提高写入性能。
本文首先介绍了HBase的写入流程,之后重点分析了WAL的写入模型以及相关优化。希望借此能够对HBase写入的高性能特性能够理解。后面一篇文章会接着介绍写入到memstore的数据如何真正的落盘,敬请期待!