RSS

HBase Architecture 101 – Storage – HBase 架构图101 存储

30 Jul

翻译http://www.larsgeorge.com/2009/10/hbase-architecture-101-storage.html

在HBase众多底层的方面中,其中一个就是数据如何存储的。然后大多数的用户可能不需要去关心。当你想学习这些各式各样的高级配置时,你只需要熟悉了解新项目的详细情况。 一旦你通过了搭建一个基本的系统的学习的阶段, 像“我如何根据我的需要来调整HBase?”, 和类似于这种问题肯定会引起你的兴趣。 另一个想了解更多的原因是在不知道什么原因下灾难到来而你需要恢复HBase的安装。

在我想知道控制各种文件的不同的类时,我画了一幅图来表述Hbase存储架构。但是,由于HBase提交者们很简单的穿梭于这个迷宫当中,我发现想保持一个一直的图是比较困难的。

下面是详细的HBase architecture解说了。

基本上,Hbase有两种文件类型。一个用来write-ahead log,另一个是真正的数据存储。文件主要被HRegionServer来控制。 但是在特定的情况下,Hmaster也得处理底层文件的操作。实际上,在被存储到HDFS中的时候,真实的文件被分成小的block。这个也是一个你可以配置系统来处理更好地较大或者较小的数据的地方。这个后面详细解释。

基本流程是:首先客户端连接zookeeper quorum(一个独立的zookeeper cluster node)找到一个特定的row key。这个为了通过从zookeeper来获得拥有-ROOT- 服务器的名字。基于这个信息,client可以查询那个服务器来得到拥有.META.表的服务器。这两个信息被cached,并且只查询一次。最后,client可以查询拥有.META表的server来获得拥有client端要查询的数据的server。

一旦client知道数据在哪里,例如:在哪个region上,它就会cache这个信息,并且直接连接拥有那个region的HRegionServer。 因此, 随着时间的过去,client就会知道从哪里去获得rows而不用再去查询.META.

注意:在HBase启动时,HMaster是负责分配region给HRegionServer的。这个也包括‘特别的’ -ROOT-和.META.

接下来,HRegionServer打开region,创建一个相应的HRegion对象。 当HRegion是’opened’状态,它就会为每一个表里面的HColumnFamily建立一个‘Store’ 实例。每一个store实例都会轮流的拥有一个或者多个StoreFile 实例。StoreFile实例是一个轻量级的HFile的包装。一个HRegion还有一个MemStore和一个HLog 实例。

Stay Put

数据是如何被真正地存储的呢?Client发送一个HTable.put 请求到存储中详细的HRegion matching信息的HRegionServer。 第一步就是决定数据是否应该先写到Write-ahead-log (WAL)中去。这个决定是取决于client端是否用了Put.writeToWAL(boolean)。 WAL是一个标准的Hadoop的SequenceFile,它存储着HLogKey。 这些key包含顺序的标号和真实的数据,它们可以用来replay那些在server crash后没有被persistent的数据。

数据一旦写入或者没有写入WAL,WAL被替换成了memstore.同时,需要检查MemStore是不是满了,如果满了,disk需要flush。如果flush请求被HRegionServer的一个单独的线程接受,它就会将数据写入HFile中,并存入HDFS。它同时也保存最后一个被写入的序列号,这样系统可以知道哪些已经被persitented了。

Files

Hbase在HDFS里面有一个可以配置的根目录,默认情况下是/hbase。 你可以使用DFS的工具来查看这些HBase stores。

/hbase/.logs 是HLog来处理的。 这个有一个子目录来对应每一个HRegionServer

oldlogfile.log 这个是在有exception的时候才会创建的文件。他们是“log split”的产物。当HMaster 启动后发现这个有log file没有被任何一个HRegionServer处理,它就会split这个log并拷贝HLogkeys到新的regions里面去。它们被直接放到region的目录下面并叫做“oldlogfile.log”。 当相应的HRegion被实例化,它就会读取这些文件,并且插入这些包含的文件到local Memstore,并启动flush来持久化这些数据并将这些文件删除。

注意:有时候你会发现又剩下的文件oldlogfile.log.old(后面多了一个old)。这个是因为HMaster多次尝试split 这个log,但是发现有另一个split log在其他的地方。在这个时候,你得看一下HRegionServer或者HMaster的log来看一下在做什么,看你是否能够删除这些文件。我偶尔发现它们是空的,因此可以安全删除。

下一组文件就是真正的regions了。每一个region名字都是使用Jenkins Hash来编码,并且有一个目录与它相对应。Hash这些region的名字的原因是他可能包含一些不能在DFS中用于path的名字。Jenkins Hash总是会返回合法的字符,并且很简单。因此你将会得到的路径:

/hbase/<tablename>/<encoded-regionname>/column-family/filename

在region的根目录下,这个也有一个.regioninfo来保存region的meta data。这个是用来重新构建破坏了的.META.表的。

在每一个column-family目录下,你能够看到真实的文件。这个会在后面详细说明。

没有提及的有拥有初始的孩子参考文件的split regions。当region里面的数据文件增长到大于配置的hbase.hregion.max.filesize时候,region就要分成两个。这个在最开始的时候很快,因为系统只是简单的创建两个refereced文件在新的region里面。这两个region分别host一半的数据。reference文件的名字是带有hosted region name作为前缀的ID。这个refereence文件只是拥有一点信息:原始的region分开的key和是不是top或者bottom 引用。重点的是这些references后来被HalfHFileReader 类用来读取原始region的数据文件。只有当compaction,原始的文件才会被重新写到新的region目录的另一个文件里面。这同时也去掉了小的reference文件和原始的文件。

这里,这个也总结了文件dump。 最后一个是看到的在每一个table目录下的compaction.dir。它们在splitting和compacting region的时候被用到。它们通常是空的,而且是被用来在交换新的数据时的一个staging 环境。

HFile

这是HBase的很低的一个level了。HFile是真正的存储文件。只是为了一个目的:快速有效地存储hbase数据。它们显然是基于Hadoop的TFile,同时模仿Google的BigTable构架的SSTable格式。之前对Hadoop MapFile在Hbase的使用没有得到很好地performance。现在看一下files的表现吧。

文件时不定长的,仅有的特定的block是FileInfo 和Trailer 块。就是这个Trailer拥有pointer到其他的block,它被写在持久化数据的后面,同时结束一个不可改变的data store。Index的block记录数据和meta block的偏移量。数据和Meta blocks实际上是可选的。但是很大可能你将在data store 文件中寻找数据。

block的大小是怎么配置的呢?它单独的被HcolumnDescriptor控制。它是轮流地在表创建的时候被指定的。

Block size 默认是:64KB。在javadoc中的解释:

“我们推荐最小的blocksize的设置在8KB到1MB之间。大的block有助于顺序访问,但是不利于random访问(因为许多的数据需要解压)。小block利于random访问,但是需要更多地内存来hold这些block index,而且可能会更慢的创建(因为我们必须在block的结尾来flush 压缩流,这个会导致文件系统的I/Oflush)。而且,鉴于压缩编码器的内部的caching,最小的block的大小可能在20KB-30KB。”

因此, 每个拥有前缀的block或者是plain的或者是被压缩过的数据。这又会怎么样呢?下面讲一下。

有一件事情可能你已经发现:Hadoop默认的block size是64MB,10倍的HBase里面的HFile。因此,看来它们并不match。因此,你需要分开来想这两个参数,并找到一个好的配置点来达到好的performance。

Hbase配置中的一个配置项:hfile.min.blocksize.size.它似乎只是在从之前的Hbase中migration时被用到。

问题:我们如何知道HFile是好还是不好?如何知道它到底存储了哪些数据呢?这里有个应用。

hbase org.apache.hadoop.hbase.io.hfile.HFile -v -p -m -f \

KeyValue’s

基本上,每一个HFile中的keyvalue都是一个简单的低层的byte 数组,它允许零拷贝的去访问数据。他们是怎么被组织的呢?

开始是两个固定长度的数字,它们表明了key和value的大小。 基于这个信息,你可以offset到数组中来直接访问数据。如果你知道你在做什么,就可以直接忽略key,如果不知道,那么需要从key里面得到需要的信息。一旦得到了keyvalue的对象,你可以通过getter来访问细节的东西。

注意:一件事情需要小心是:keyvalue.getkey()和keyvalue.getrow()的不同。前者是得到所有的关于row key,column,timestamp。而后者才是只得到rowkey,然后通过它得到整个row的信息。这个有点confused。

到此为止,翻译完毕。许多的意思不是很通顺,还需要update。

Advertisements
 
Leave a comment

Posted by on July 30, 2012 in HBase

 

Tags:

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: