概述
HBase是基于Bigtable论文的面向列的分布式存储系统,其存储设计是基于Memtable/SSTable的。其它如Cassandra都是采用的该设计。
整个存储分为两部分,一部分为内存中的MemStore(Memtable),另外一部分为磁盘(这里是HDFS)上的HFile(SSTable)。下面分别讲述两种类型的存储分别的实现:
MemStore中最重要的变量是:
volatile KeyValueSkipListSet kvset;
这里的KeyValueSkipListSet里面实际是这样的:
private final ConcurrentNavigableMap<KeyValue, KeyValue> delegatee;
换句话说,其实就是一个放内存的Map存放着kv。
HFile是HBase实际的文件存储格式,它是基于TFile的文件格式,替换了早期的MapFile,改进了性能。
然后HBase会进行控制,当MemStore写满了以后进行刷磁盘操作。
而HLog是HBase的日志格式实现,主要是在写入的时候进行write-ahead-log,主要为recovery而做。
HFile
HFile是HBase中实际存数据的文件,为HBase提供高效快速的数据访问。它是基于Hadoop的TFile,模仿Google Bigtable 架构中的SSTable格式。之前的Hadoop的MapFiles已经被证明性能不能到达我们的期望。文件格式如下:
文件是变长的,唯一固定的块是File info和Trailer,如图所示,Trailer有指向其它块的指针,这些指针也写在了文件里,Index块记录了data和meta块的偏移量,data和meta块都是可选的。
块的大小是由表创建时的HColumnDescriptor指定的,如下是master web interface上看到的一个例子:
{NAME => 'docs', FAMILIES => [{NAME => 'cache', COMPRESSION => 'NONE', VERSIONS => '3', TTL => '2147483647', BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'false'}, {NAME => 'contents', COMPRESSION => 'NONE', VERSIONS => '3', TTL => '2147483647', BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'false'}, ...
默认的大小是64KB,下面是一段关于HFile的解释:
“最小的块大小。我们建议通常将其设置为8KB到1MB之间,如果经常进行基于primary key的顺序访问,可以设置更大的块大小,但是这样会带来低效的随机访问效率(更多的数据需要被解压缩)。更小的块大小将带来更好的随机访问效率,但是会耗费更多的内存去维持索引,并且在创建的时候会比较慢,因为需要flush压缩流,这将导致一个FS I/O flush。由于内部的压缩codec的缓存,最小的块大小可以为20-30KB”
上图是HFile中每个KeyValue的格式,和普通的key-value没有太大的区别。
HLog
源码中的实现类是HLog。每个HRegionServer会对应一个HLog,HRegion在初始化的时候HRegionServer会将该HLog的实例作为构造函数传入其中。HLog的核心函数是其append()函数。
在HLog中,会维持一个Sequence Number,是一个AtomicLong型,当一个Region启动的时候会从HFile的Meta field里面读出当前的Sequence Number。
如图所示,由于HLog是Region之间共享的,因此,log的顺序是不定的,这一点后面会提到。当一个HRegionServer崩溃掉后,HMaster会让HRegionServer重启的时候根据日志进行恢复。
当前的WAL使用的是Hadoop下的SequenceFile格式,其key是HLogKey实例,它包括以下内容:
- private byte [] encodedRegionName;
- private byte [] tablename;
- private long logSeqNum;
- // Time at which this edit was written.
- private long writeTime;
- private byte clusterId;
private byte [] encodedRegionName;private byte [] tablename;private long logSeqNum;// Time at which this edit was written.private long writeTime;private byte clusterId;
由于操作系统处理批量的数据要块过单个单个的处理,因此,需要进行flush日志。LogFlusher实现了该功能,它调用了HLog.optionalSync(),它将检查是否到了hbase.regionserver.optionallogflushinterval,默认是10秒。
日志的大小会有一个限制,这是用hbase.regionserver.logroll.period参数控制的,默认是1个小时。到点以后LogRoller会触发操作,检查当前的Sequence Number,看小于它的所有日志是否完整。
参考文献
http://www.larsgeorge.com/
主要参考自larsgeorge的hbase系列文章