HBase使用一个HDFS中可配置的根目录,默认设为“/hbase”。可以使用hadoop fs -lsr /hbase查看目录结构,文件能够被分为两类,一类位于HBase根目录下,另外一类位于根目录中的表目录下。算法
Flush命令能够将内存中的数据写到存储文件中,不然就必须等插入的数据达到配置的刷写大小。shell
第一组文件是被HLog实例管理的WAL文件,这些日志文件被建立在HBase的根目录下一个名为.logs的目录中。对于每一个HRegionServer,日志目录中都包含一个对应的子目录。在每一个子目录中有多个HLog文件(由于日志滚动)。一个region服务器的全部region共享同一组HLog文件。安全
因为HDFS使用内置的append机制来追加写入日志文件,因此只有等到文件大小达到一个完整的块时,文件对用户才是可见的—包括hadoop fs -lsr命令。虽然put操做的数据被安全持久化了,但当前写入日志文件的数据大小是稍微偏离的。服务器
当全部包含的修改都被持久化到存储文件中,从而再也不须要日志文件时,它们会被放到HBase根目录下的.oldlogs目录中。当条件知足配置上的阈值会触发日志的滚动。在十分钟后,旧的日志文件将被master删除,这是经过hbase.master.logcleaner.ttl属性设置的。Master多线程
在HBase中,每张表都有本身的目录,其位于文件系统中HBase根目录下。每张表目录包括一个名为.tabledesc的顶层文件,该文件存储表对应序列化后的HTableDescriptor。其中包括表和列族的定义,同时其内容也能够被读取。app
在每张表的目录里面,表模式中每一个列族都有一个单独的目录。目录的名字是一部分region名字的MD5散列值。负载均衡
Region文件的整体结构是异步
/<hbase-root-dir>/<tablename>/<encoded-regionname>/<column-family>/<filename> /apps/hbase/data/data/hbase/namespace/d25b2b8cb0d3d1c538437c1b89b8f8c8/info/7aef732982e04b77b7ee2a0ef9d7adc3
能够在每一个列族目录中看到实际的数据文件。工具
Region目录中也有一个.regioninfo文件,这个文件包含了对应region的HRegionInfo实例序列化后的信息。与.tabledesc文件相似,它能被外部工具用来查看region的相关信息。例如,HBase的hbck工具就用它来检查并生成元数据表中丢失的条目。oop
可选的.tmp目录是按需求建立的,它被用来存放临时文件,例如,一次合并的重写文件。一旦这个过程完成,这些临时生成的文件一般会被移到region目录中。
在WAL回放时,任何未提交的修改都会被写入到每一个region的一个单独的文件中。以上是第一步,而后若是日志拆分过程已经成功完成,这些文件将被自动移动到临时的recovered.edits目录中。当region被打开时,region服务器将会看到须要恢复的文件,而且回放其中相应的条目。
一旦region超过了配置中region大小的最大值,region就须要拆分,其会建立一个对应的splits目录,它被用来临时存放两个子region相关的数据。若是拆分过程成功,以后它们会被移动到表目录中,并造成两个新的region,每一个region表明原始region的一半。
当看到一个region的目录中没有.tmp目录,这就意味着尚未进行过压缩操做。若是没有recovered.edits目录,这就意味着WAL尚未进行过回放操做。
当一个region里的存储文件增加到大于配置的hbase.hregion.max.filesize大小或者在列族层面配置的大小时,region会被一分为二。这个过程一般很是迅速,由于系统只是为新region建立了两个对应的文件,每一个region是原始region的一半。
Region服务器经过在父region中建立splits目录来完成这个过程。接下来关闭该region,此后这个region再也不接受任何请求。
而后region服务器经过在splits目录中设立必须的文件结构来准备新的子region(使用多线程),包括新region目录和参考文件。若是这个过程成功完成,它将把两个新region目录移到表目录中。.META.表中父region的状态会被更新,以表示其如今拆分的节点和子节点是什么。以上过程能够避免父region被意外从新打开。
两个子region都准备好后,将会被同一个服务器并行打开。打开的过程包括更新.META.表,这样能够把两个region像其余region同样做为可用region列出来。以后这两个region会上线并开始服务请求。
同时也会初始化为两个region并对region中的内容执行合并,合并过程在替换引用文件以前会把父region的存储文件异步重写到两个子region中。以上过程会在子region的.tmp目录中执行。一旦生成了重写以后的文件,它们将自动取代引用文件。
最终父region会被清理掉,这意味着它在.META.表中的表项会被移除,而且它在磁盘上全部的文件都会被删除。最后,master被告知关于拆分的状况,而且能够因为负载均衡而把新region移动到其它region服务器上。
在拆分过程当中,全部的步骤都在ZK中进行跟踪。这使得在一个服务器失效时,其它进程能够知道这个region状态。
存储文件会被后台的管理进程仔细地监控起来以确保它们处于控制之下。随着memstore的刷写会生成不少磁盘文件。若是文件的数目达到阈值,合并(compaction)过程将把它们合并成数量更少的体积更大的文件。这个过程持续到这些文件中最大的文件超过配置的最大储存文件大小,此时会触发一个region拆分。
压缩合并有两种,即minor和major。Minor合并负责重写最后生成的几个文件到一个更大的文件中。文件数量是由hbase.hstore.compaction.min属性设置的。它的默认值为3,而且最小值须要大于或等于2.过大的数字将会延迟minor合并的执行,同时也会增长执行时消耗的资源及执行的时间。
Minor合并能够处理的最大文件数量默认为10,用户能够经过hbase.hstore.compaction.max来配置。Hbase.hstore.compaction.min.size(默认设置为region的memstore刷写大小)和hbase.hstore.compaction.max.size(默认设置为Long.MAX_VALUE)配置项属性进一步减小了须要合并的文件列表。任何比最大合并大小大的文件都会被排除在外。最小合并大小的功能稍有不一样:它是一个阈值,而不是每一个文件的限制。它包括全部小于限制的文件,直到到达每次压缩容许的总文件数量。
算法使用hbase.hstore.compaction.ratio(默认为1.2,或者120%)来确保在选择过程当中包括足够的文件。通过跟新文件总的存储文件比较以后,这个比例仍将选择达到那个值的文件。评估老是按照从老文件到新文件的顺序来进行的,这样能够确保更老的文件首先被合并。这些属性的组合容许用户微调一个minor合并包括文件的数量。
HBase支持的另一种合并是major合并:它们把全部文件压缩成一个单独的文件。在执行压缩检查时,系统自动决定运行哪一种合并。在memstore被刷写到磁盘后会触发检查,或在shell命令compact、major_compact以后触发检查,或是相应API在被调用后触发检查,抑或是被一个异步的后台进程触发后。Region服务器运行这个进程,而其功能由CompactChecker类实现,它以一个固定的周期触发检查,这个周期由hbase.server.thread.wakefrequency参数控制(乘以hbase.server.thread.wakefrequency.multiplier,设为1000,这样它的执行频率不会像其余基于线程的任务那么频繁)。
除非用户使用shell命令major_compact或者使用majorCompact()这个API,将强制运行major合并,不然服务器会首先检查上次运行到如今是否达到hbase.hregion.majorcompaction(设为24小时)指定的时限。Hbase.hregion.majorcompaction.jitter(设为0.2,即20%)参数会将全部存储的时间周期分开。若是没有这个抖动,全部的存储文件都将在每24小时的同一时间运行一个major合并。
若是尚未达到major合并的执行周期,系统会选择minor合并执行。基于以上配置属性,服务器将检查是否有足够的文件供minor合并执行,若是有就继续。
当minor合并包括全部的存储文件,且全部文件均未达到设置的每次压缩的最大文件数时,minor合并可能被提高成major合并。