1、 结构
HBase中的每张表都通过行键按照一定的范围被分割成多个子表(HRegion),默认一个HRegion超过256M就要被分割成两个,由HRegionServer管理,管理哪些HRegion由HMaster分配。
HRegionServer存取一个子表时,会创建一个HRegion对象,然后对表的每个列族(Column Family)创建一个Store实例,每个Store都会有0个或多个StoreFile与之对应,每个StoreFile都会对应一个HFile, HFile就是实际的存储文件。因此,一个HRegion有多少个列族就有多少个Store。
另外,每个HRegion还拥有一个MemStore实例。
(发现上图中一个错误,一个HRegionServer只对应一个HLog,也就是说同个HRegionServer中的HRegion共享一个HLog)
2、 流程
a) Client发起了一个HTable.put(Put)请求给HRegionServer
b) HRegionServer会将请求匹配到某个具体的HRegion上面
c) 决定是否写WAL log。WAL log文件是一个标准的Hadoop SequenceFile,文件中存储了HLogKey,这些Keys包含了和实际数据对应的序列号,主要用于崩溃恢复。
d) Put数据保存到MemStore中,同时检查MemStore状态,如果满了,则触发Flush to Disk请求。
e) HRegionServer处理Flush to Disk的请求,将数据写成HFile文件并存到HDFS上,并且存储最后写入的数据序列号,这样就可以知道哪些数据已经存入了永久存储的HDFS中。
二、 Hbase存储格式HBase是基于BigTable的面向列的分布式存储系统,其存储设计是基于Memtable / SSTable设计的,主要分为两部分,一部分为内存中的MemStore (Memtable),另外一部分为磁盘(这里是HDFS)上的HFile (SSTable)。还有就是存储WAL的log,主要实现类为HLog.
MemStoreMemStore源码:private final ConcurrentNavigableMap
本质上MemStore就是一个内存里放着一个保存KEY/VALUE的MAP,当MemStore(默认64MB)写满之后,会开始刷磁盘操作。
HFile结构
HFile是基于HADOOP TFile的,如图2,文件长度为变长,仅FILE INFO/Trailer定长,Trailer中有指针指向其他数据块的起始点。Index数据块记录了每个Data块和Meta块的起始点。Data块和Meta块都是可有可无的,但对于大多数HFile,你都可以看到Data块。
Data块中,除了头部的MAGIC之外,就是一对对KEY/VALUE对,结构如下:
关于文件块的大小:
默认块大小64KB,在创建表时可以通过HColumnDescriptor设定每个Family的块大小。
大数据块:适合顺序查找,不适合随机查找。
小数据块,适合随机查找,需要更多内存保存Data Index,创建文件慢,更多的flush操作
图4 HFile总体结构图
当key的value大小超过BLOCK SIZE时,那么查找这些value就无法通过索引去快速查找,而是需要通过遍历进行。
另外,针对目前针对现有HFile的两个主要缺陷:
a) 暂用过多内存
b) 启动加载时间缓慢
提出了HFile Version2设计:
https://issues.apache.org/jira/secure/attachment/12478329/hfile_format_v2_design_draft_0.1.pdf
HLogHLog是HBase的日志类,在写入时进行write-ahead-log(WAL),主要为数据恢复。每个HRegionServer会对应一个HLog实例,HRegion在初始化的时候HRegionServer会将该HLog的实例作为构造函数传入其中。HLog的核心函数是其append()函数。
HLog File 是一个Sequence File,只能在文件的末尾添加内容。除了文件头以外,HLog File 由一条条HLog.Entry构成。Entry是HLog的基本组成部分,也是Read /Write的基本单位。
Entry由两个部分组成:HLogKey和WALEdit。
HLogKey主要包含以下几个变量:
private byte [] encodedRegionName;
private byte [] tablename;
private long logSeqNum; // Time at which this edit was written.
private long writeTime;
private byte clusterId;
logSeqNum是HLog 类的一个属性,AtomicLong类型。每写一个Entry就自动加1. 由于RS和HLog是一一对应的,所以logSeqNum是在RegionServer范围内的自增量。
WALEdit的主体是一个KeyValue的List。在旧的版本中WALEdit只包含一个KeyValue,然而新的版本可以包含多个KeyValue,而且这些KeyValue可以拥有不同的RowKey。
图5 HLog文件结构图
三、 HBase 预写日志 (WAL)
目前的HBase新版本没有实现二级索引,以前hbase的版本有过二级索引,实现时也是写写数据再写索引,索引放到后台队列中异步地写。实现最终一致。
HBase移走这块代码的一个原因就是索引到底落后数据多少时间是不确定的,特别是在异常的情况下。这样就导致在清理HLog时无法确定一个HLog是否真正全部写入数据了。
关于HBase二级索引的一篇讨论文章,参考:
问题:为什么一个RegionServer 对应一个HLog,而不是一个region对应于一个log file?
BigTable论文答:如果每一个”tablet”(对应于HBase的region)都提交一个日志文件,会需要并发写入大量的文件到GFS(对应HDFS),这样,根据每个GFS server所依赖的文件系统,写入不同的日志文件会造成大量的磁盘操作。
HBase依照这样的原则。在日志被回滚和安全删除之前,将会有大量的文件。如果改成一个region对应于一个文件,将会不好扩展,会引发问题。