目前主流的RDBMS和其他非关系型数据库底层数据库结构大都会使用B树或B+树,有时还会使用LSM树(Log-Structured Tree)。HBase与BitTable一样,是基于LSM树的系统。
为了了解HBase存储层,下图给出了一个顶层结构图。
从上图中可以看到HBase主要处理两种文件,预写日志(WAL)
,实际文件
。这两种文件主要由HRegionServer管理。在某些情况下,HMaster也可以进行一些低层的文件操作。当存储数据到HDFS中时,数据文件会被切分成更小的块。
一个基本的流程分为三步:
ZooKeeper
查询到含有-ROOT-
的region server A.META.
表中对应的region server B事实上,查找region服务器A和region服务器B的主要内容(比如region的位置)都被缓存下来了,因此只会被查询一次,同时直接联系管理实际数据HRegionServer。所以之后客户端可以直接通过缓存信息很好的定位所需的数据位置,而不用再次查找.META.
表。
-ROOT-
存在于zookeeper上,类似于RDBMS里的database这个概念,可以更好的做权限管理和安全控制; .META.
的信息作为一种Region存放在Region Server上。
在新版本的HBase里面,-ROOT-
表已经改名为hbase:namespace
,.META.
表改名为hbase:meta
。
启动zookeeper client,可以使用ls /hbase/namespace
查询得到
启动hbase shell,可以使用scan 'hbase:namespace'
查询得到
启动hbase shell,可以使用scan 'hbase:meta'
查询得到
.
├── habse.version
├── hbase.id
├── .logs
├── .corrupt
├── splitlog
└── data
└── default
├── t1
│ ├── f1f21fee947157f277b042f11d127e82
│ │ ├── .regioninfo
│ │ ├── c1
│ │ │ └── 5201b34c76d646c2870c58949f54e776
│ │ └── splits
│ ├── .tabledesc
│ │ └── .tableinfo.0000000001
│ └── .tmp
└── testtable
├── 2e1f76c4bf66f2e2c16cff1b1a676bbd
│ ├── .regioninfo
│ ├── colfam1
│ ├── recovered.edits
│ │ └── 2.seqid
│ ├── splits
│ └── .tmp
├── .tabledesc
│ └── .tableinfo.0000000001
└── .tmp
HBase使用一个HDFS中可配置的根目录,用户可以使用hdfs
命令来查看,也可以使用hue这款工具来查询,这里使用hue进行示范。
文件可以被分为两类,一类位于HBase根目录下,另一类位于根目录中的表目录下。
目录/文件名 | 用途 |
---|---|
wals | 由HLog实例管理,存放在“目录结构”中的.log目录中 |
hbase.id | 文件包含集群唯一的ID和文件格式版本信息 |
hbase.version | 文件格式版本信息 |
splitlog | 存储日志拆分过程中产生的中间拆分文件 |
corrupt | 存储日志拆分过程中产生的中间损坏日志 |
在HBase中,每张表都有自己的目录,其位于文件系统的HBase根目录下。
目录/文件名 | 用途 |
---|---|
.tabledesc | 存储相应的表序列化后的HTableDescriptor |
.tmp | 临时数据,比如更新.tableinfo文件时生成的临时数据 |
md5.. | region级文件 |
.tableinfo文件在.tabledesc目录里面
region级文件位于表目录里面。region级文件的目录时一部分region名字的MD5散列值。
目录/文件名 | 用途 |
---|---|
.regioninfo | 对应region的HRegionInfo实例反序列化的信息 |
recovered.edits | 存放临时的拆分文件 |
- 在WAL回收时,任何未提交的修改都会被写入到每个region的一个单独的文件(splitlog)中。
- 将上述文件移动到临时的recovered.edits文件中
- 若region被打开,region服务器将会看到需要恢复的文件,并放回其中对应的条目
一旦region超过了配置中region大小的最大值,region就需要拆分,其会创建一个对应的splits目录,它被用来临时存放两个region相关的数据。如果拆分成功(通常这个过程持续几秒或更短),之后它们会被移动到表目录中,并形成两个新的region,每个region代表原始值的一半。
当用户看到region的目录中:
- 没有.tmp目录,意味着还没有进行过压缩操作
- 没有recovered.edits目录,意味着WAL没有进行过回放操作
在拆分过程中,所有的步骤在ZooKeeper中都进行跟踪,这使得一个服务器失效时,其他进程可以直到这个region的状态。
存储文件会被后台的管理进程仔细的监控起来以确保它们处于控制值夏,随着memstore的写入会生成很多的磁盘文件,如果文件的数目达到阈值,合并过程将会把它们合并成数量更少,体积更大的文件。这个过程持续到这些文件超过配置的最大存储文件大小,会触发一个region拆分
压缩合并有两种,即minor和major。
minor合并可以处理的最大文件数量默认是10,用户可以通过hbase.hstore.compaction.max来配置。
如果海没有达到major合并的执行周期,系统会选择minor合并执行。当minor合并包括所有的存储文件,且所有文件均未达到设置的每次压缩的最大文件数时,minor合并可能被提升成major合并。
实际的存储文件功能是由HFile类实现的,它被专门创建达到一个目的:有效的存储HBase的数据。它们基于Hadoop的TFile类。HFile参考BigTable的SSTable和Hadoop的TFile实现,从HBase开始到现在,HFile经历了三个版本,其中V2在0.92引入,V3在0.98引入。这里我们看下V1的格式。
这些文件是可变长度的,唯一固定的块是File Info块和Trailer块。如上图所示:
Trailer有指向其他块的指针,它是在持久化数据到文件结束时写入的,写入后即确定其成为不可变的数据存储文件。
Index块记录Data和Meta块的偏移量。
Data和Meta块实际上都是可选的,但是考虑到HBase如何使用数据文件,在存储文件中用户几乎总能找到Data块。
在HDFS中,文件的默认块大小是64MB(现默认是128MB),这个是HFile默认块大小的1024倍,因此HBase存储文件的块与Hadoop的块之间并没有匹配关系。事实上,这两种块类型之间根本没有相关性。HBase把它的文件透明的存储到文件系统中,而HDFS也使用块来切分文件仅仅是一个巧合,并且HDFS并不知道HBase中存储的是什么。
下图描述了逻辑上把一个单元格的数据存储到一张表中,到实际存储到HDFS的映射过程。
KeyValue类是HBase中数据存储的核心,由keylength
、valuelength
、key
、value
四个部分组成。
其中Key又由Row Length
、Row、Column Family Length
、Column Family
、Column Qualifier
、Time Stamp
、Key Type
七部分组成。
KeyValue不会在块之间拆分。例如,如果有一个8 MB的KeyValue,即使块大小是64kb,这个KeyValue将作为一个连续块读取。
Key | 作用 |
---|---|
Row Length | 存储rowkey的长度,占2B (Bytes.SIZEOF_INT) |
Row | 存储Rowkey实际内容,其大小为Row Length |
Column Family Length | 存储列簇Column Family的长度,占1B (Bytes.SIZEOF_BYTE) |
Column Family | 存储Column Family实际内容,大小为Column Family Length |
Column Qualifier | 存储Column Qualifier对应的数据 |
Time Stamp | 存储时间戳Time Stamp,占8B (Bytes.SIZEOF_LONG) |
Key Type | 存储Key类型Key Type,占1B ( Bytes.SIZEOF_BYTE),Type分为Put、Delete、DeleteColumn、DeleteFamilyVersion、DeleteFamily、Maximum、Minimum等类型,标记这个KeyValue的类型 |
由于Key中其它的字段占用大小已经知道,并且知道整个Key的大小,因此没有存储Column Qualifier的大小。
对于Put: rowkey=row1, cf:attr1=value1
操作,Key对应关系如下:
key | value |
---|---|
rowlength | 4 |
row | row1 |
columnfamilylength | 2 |
columnfamily | cf |
columnqualifier | attr1 |
timestamp | server time of Put |
keytype | Put |