HBase架构简介
一、概览
图1
图1指出了在HBase中其实有两种文件,一种是write-ahead log,而另一种则是真正存储数据的地方。而这些文件都被HRegionServers来处理。从图中可以看出,文件最后在HDFS中存在不同的block中。
基本的通信流程是这样的:当一个client需要查询某一特点的row时,它会先连到Zookeeper(事实上是ZK
管理的集),并且从zookeeper获取持有-ROOT-的region的server name,有了这个信息以后,我们就可以去寻包含刚刚我们查询的row的.META.的region的server name。这些信息都会被客户端缓存下来。最后,我们可以通过.META.到持有我们所查询的rowkey的region。一旦获取了这个row在哪个region中,这个信息也会被客户端缓存下来,所以下次再访问的时候,就可以直接访问那个region了。过了一段时间后,客户端就会搜集到相当全面的信息:查询某row时应到哪个region去,从而不需要去查询.META.了。
当HBase启动时,HMaster负责向各个RS分配region,其中当然也包括了-ROOT-和.META.某RS打开一个Region,这时会创建一个相应的HRegion对象,当HRegion打开时,它会为每一个HColumnFamily创建一个Store,每一个Store 都会有一个或者多个StoreFile的实例,它是对真正的存储文件HFile的轻量级封装。每一个Store都会有一个MemStore,并且整个RS会共享一个HLog实例。
二、写
当一个client向RS发起一个HTable.put(Put)的请求时,第一步先把数据写入write-ahead-log(WAL),被HLog所表示,这个WAL是一个标准的Hadoo SequenceFile,当Server挂掉以后再重启,WAL可以继续将未被持久化的数据持久化。
一旦数据被写入WAL,它就被放到MemStore中,与此同时,会检查MemStore 是否已经满了,假如已
经满了,那么就需要把MemStore中的数据flush到磁盘上,这个动作由RS中一个独立的线程来完成,它会把数据存入HDFS上的一个新的HFile中。同时它也会记录最后一次被写入的Sequence number,这样就能知道到目前为止,哪些数据已经被持久化了。
HBase在HDFS有一个可配置的目录,默认是/hbase,你可以使用hadoop fs –lsr 查看hbase所存储的各个文件。
首当其冲的就是WAL文件,它们被放在根目录下的.logs文件夹下,假如我这边有6台rs组成的测试环境,结构如下:
每个rs的子目录中存有若干个HFiles。为什么会有多个?(答:log rotation)
hbase为什么查询快这台rs上的所有region共享相同的HLog files。我们注意到有log文件的大小为0,这其实是在这个文件刚刚创建时相当正常的情况。因为hdfs用它内置的append 操作来向文件里写,只有当这个block写完的时候才会让客户端看到。
当一个log文件的所有数据都被持久化后而不再需要被使用时,它就会被放到.oldlogs中,同样的,这个目录也在根目录下。
Master十分钟后就来删除这些oldlogs,并且每分钟都会检查(可配置)。
在hbase中的每一个表都会在根目录下有一个自己的目录。在每个目录下存有组成该table的region
当一个region中的storefiles的大小大于hbase.hregion.max.filesize的值时(或者通过FColumnDescriptor来设置column family大小),region就会被分裂成两个(在原region目录下直接生成),理论上来讲,这个过程非常快,因为只是为两个新的region指定了到stoefiles的不同引用。完后,rs就会把原来的那个region 给关掉,不会再响应任何请求。接下来rs会在split这个目录下设置必要的数据结构,包括新region的目录和引用的文件,并且.META.表也会做好更新,最后这两个region的目录就会被搬到所属表的根目录下。
有split,当然也会有compaction,storefiles被一个后台线程所监控,已经到memstore装满后会flush到磁盘成为storefiles,当这些storefiles多到一定程度时,compaction就会把它们组合成更少、更大的文件。这个过程一直将进行下去直到最大的那个文件超过了我们所配置的文件的最大值,这同时也会触发一次split 事件。有两种类型的compaction:minor和major。minor compaction只是负责将最近的几个文件合并成一个大文件。文件的数量可以通过配置hbase.hstorepaction.min属性来配置(之前被称为hbase.
hstorepactionThreshold,虽然被deprecated了,但是还是支持的).超过这个数量就会触发compaction。假如将这个值设的太大的话,那么就会延迟进行minor compaction,并且在compaction进行时需要更多的资源和更长的时间。在一次compaction中最多包含的文件由hbase.hstorepaction.max来指定,默认为10. Hbase.hstorepaction.max.size用来配置可以compaction的文件的最大大小。任何超过这个大小的文件都将被排除在compaction之外。不同于minor compaction,major compaction 会compact所有文件到一个单独的文件中。Compaction的类型在compaction check阶段就确定好了,以下几个事件都会触发check:有memstore被flush到磁盘,在shell里使用了compact或者major_compact 命令,调用了相应的api,后台线程触发。这个后台线程调用CompactionChecker,每台rs都会起一个单独的实例,它按照hbase.server.thread.wakefrequency配置的(乘以hbase.server.thread.wakefrequency.multiplier)时间来进行轮询。如果你使用了major_compact命令,或者调用了majorCompact方法,那么hbase就会强行进行一次major compaction. 否则,先检查major compaction是否已经到期,(hbase.hregion.majorcompaction)。假如major compaction没有到期,那么就去检查是否符合minor compaction的条件,如符合,则进行minor compaction。
下面接着来看Write-Ahead Log。rs将数据保存在内存中,知道达到阈值后再flush到磁盘上,这样可以避免产生很多小文件。然而,因为文件时存在内存中的,这就意味着当rs挂掉时有可能有丢失数据的风险,譬如,断电-囧。我们通常使用write-ahead logging来解决这个问题,每个update(或者说edit)都会
被写到log,当通知客户端成功后,rs把数据再加载到内存中。WAL是灾难发生时的生命线,类似于mysql的binlog,wal记录了所有数据的变化情况。假如server 挂了,WAL能有效的让情况恢复到crash发生前。这也就是说当写入WAL失败时,必须认为这次写入是失败的。
上图的流程可以描述如下:首先客户端发起一个修改数据的请求,可以是
put(),delete(),increment(), 所有的这些修改都会被封装到一个KeyValue的对象中并且通过RPC调用。一旦这个KeyValue实例到了,它会被路由到包含所请求的row的region,由HRegion对象表示。数据先被写到WAL,再被写到memstore。最后,当memstore达到一定大小后,或者在一定的时间后,数据就会被持久化到磁盘上。而在这个之前,数据一直存在memstore中,随时有丢失的风险。WAL保证了数据
不会丢失,就算这台服务器挂了也不会,因为WAL是存在hdfs上,在别的机子上还有备份,其它任何一点服务器都能打开log来恢复数据。
三、读
HBase利用多个storefiles存储column family,storefiles是不可变的,所以没有办法去删除一个特定的值,只会为删除的地方做个标记,这个地方可以是一个单独的cell,可以是一系列的cells,也可以是一整行。在查一个特定的entry 前,会有一次预扫描(exclusion check),它会根据时间戳及Bloom filter来去除那些肯定不包含目标entry的文件,最后会从剩下的文件,包括memstore中来查询。假如查询的时候,我们只查询了特定的family column,那么其他的column family也会在查询中被忽略。
下面我们再来具体谈下Region的查的细节。前面已经提到,到客户端去查某个或者某段row key时,需要先去查-ROOT-和.META. , ROOT表指向了所有META表,hbase只存在一个ROOT表,并且不会split。Zk中存了含有ROOT 表的rs的名字,接下来又通过ROOT查到META,最后通过META到特定的表的region。并不是每次查都要这么麻烦的,因为客户端会缓存住region的位置,经过一段时间后,客户端上缓存的信息已经比较丰富了,基本上可以直接通过缓存来访问region了。客户端的采用了递归访问的方法,首先客户端会去检查缓存,假如说缓存已经过期,或者region已经split,merge或者move掉了,这时客户端会去询问相应rs的META表,假如META表无效,那么客户端会再上一
层去问ROOT表关于META表的信息,假如ROOT表无效,那么客户端需要去询问ZK,ROOT表到底在哪个位置。
To be continued.
Cc

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。