HBase学习总结(3):HBase的数据模型及工作机制

一、HBase数据模型
HBase模式里的逻辑实体包括:
(1)表(table):HBase用表来组织数据。表名是字符串(String),由可以在文件系统路径里使用的字符组成。
(2)行(row):在表里,数据按行存储。行由行键(rowkey)唯一标识。行键没有数据类型,总是视为字节数组byte []。
(3)列族(column family):行里的数据按照列族分组,列族也影响到HBase数据的物理存放,因此,它们必须事前定义并且不轻易修改。表中每行拥有相同列族,尽管行不需要在每个列族里存储数据。列族名字是字符串(String),由可以在文件系统路径里使用的字符组成。
(4)列限定符(column qualifier):列族里的数据通过列限定符或列来定位。列限定符不必事前定义,列限定符不必在不同行之间保持一致。就像行键一样,列限定符没有数据类型,总是视为字节数组byte []。
(5)单元(cell):行键、列族和列限定符一起确定一个单元。存储在单元里的数据称为单元值(value)。值也没有数据类型,总是视为字节数组byte []。
(6)时间版本(version):单元值有时间版本。时间版本用时间戳标识,是一个long。没有指定时间版本时,当前时间戳作为操作的基础。HBase保留单元值时间版本的数量基于列族进行配置,默认数量是3个。
HBase的每个数据值使用坐标来访问。一个值的完整坐标包括行键、列族、列限定符和时间版本。由于把所有坐标视为一个整体,因此HBase可以看作是一个键值(key-value)数据库。

例如,在《HBase学习总结(2):HBase介绍及其基本操作》(http://blog.csdn.net/zhouzhaoxiong1227/article/details/46682291)中:
(1)表名是“mytable”。
(2)行是“first”、“second”和“third”。
(3)列族是“cf”。
(4)列限定符是“info”、“name”和“nation”。
(5)单元值是“hello hbase”、“zhou”和“China”。
(6)时间戳是“1435548279711”、“1435548751549”和“1435548760826”。

二、HBase工作机制
1.HBase写路径
在HBase中无论是增加新行还是修改已有的行,其内部流程都是相同的。默认情况下,执行写入时会写到两个地方:预写式日志(write-ahead log,WAL,也称HLog)和MemStore。HBase的默认方式是将写入动作记录在这两个地方,以保证数据持久化。只有当这两个地方的变化信息都写入并确认之后,才认为写动作完成。写入过程如图1所示。
这里写图片描述
图1 HBase同时向WAL和MemStore执行写入
MemStore是内存里的写入缓冲区,HBase中数据在永久写入磁盘之前在这里积累。当MemStore填满之后,其中的数据会刷写到硬盘,生成一个HFile。HFile是HBase使用的底层存储格式。HFile对应于列族,一个列族可以有多个HFile,但一个HFile不能存储多个列族的数据。在集群的每个节点上,每个列族有一个MemStore。MemStore生成HFile的过程如图2所示。
这里写图片描述
图2 MemStore生成HFile
如果MemStore还没有刷写,服务器就崩溃了,内存中没有写入硬盘的数据就会丢失。HBase的应对办法是在写动作完成之前先写入WAL。HBase集群中每台服务器维护一个WAL来记录发生的变化。WAL是底层文件系统上的一个文件。直到WAL新记录成功写入后,写动作才会被认为成功完成。这可以保证HBase和支撑它的文件系统满足持久性。大多数情况下,HBase使用Hadoop分布式文件系统(HDFS)来作为底层文件系统。
如果HBase服务器宕机,没有从MemStore里刷写到HFile的数据将可以通过回放WAL来恢复。你不需要手工执行。HBase的内部机制中有恢复流程部分来处理。每台HBase服务器有一个WAL,这台服务器上的所有表(和它们的列族)共享这个WAL。
值得注意的是,不写入WAL会在RegionServer故障时增加丢失数据的风险。关闭WAL,出现故障时HBase可能无法恢复数据,没有刷写到硬盘的所有写入数据都会丢失。

2.HBase读路径
如果想快速访问数据,通用的原则是数据保持有序并尽可能保存在内存里。HBase实现了这两个目标,大多数情况下读操作可以做到毫秒级。HBase读动作必须重新衔接持久化到硬盘上的HFile和内存中MemStore里的数据。HBase在读操作上使用了LRU(最近最少使用算法)缓存技术。这种缓存也叫做BlockCache,和MemStore在一个JVM堆里。BlockCache设计用来保存从HFile里读入内存的频繁访问的数据,避免硬盘读。每个列族都有自己的BlockCache。
掌握BlockCache是优化HBase性能的一个重要组成部分。BlockCache中的Block是HBase从硬盘完成一次读取的数据单位。HFile物理存放形式是一个Block的序列外加这些Block的索引。这意味着,从HBase里读取一个Block需要先在索引上查找一次该Block,然后从硬盘读出。Block是建立索引的最小数据单位,也是从硬盘读取的最小数据单位。Block大小按照列族设定,默认值是64KB。根据使用场景你可能会调大或者调小该值。Block变小会导致索引变大,进而消耗更多内存;Block变大意味着索引项变少,索引变小,因此节省内存。
从HBase中读出一行,首先会检查MemStore等待修改的队列,然后检查BlockCache看包含该行的Block是否最近被访问过,最后访问硬盘上的对应HFile。整个读入过程如图3所示。
这里写图片描述
图3 HBase读入过程
注意,HBase存放某个时刻MemStore刷写时的快照,一个完整行的数据可能存放在多个HFile中。为了读出完整行,HBase可能需要读取包含该行信息的所有HFile。

3.HBase的合并
删除命令并不是立即删除内容,它只是给记录打上了删除的标记。就是说,针对那个内容的一条“墓碑”(tombstone)记录写入进来,作为删除的标记。墓碑记录用来标志删除的内容不能在get和scan命令中返回结果。因为HFile文件是不能改变的,直到执行一次大合并,这些墓碑记录才会被处理,被删除记录占用的空间才会被释放。
合并分为两种:大合并(major compaction)和小合并(minor compaction)。两种将会重整存储在HFile里的数据。小合并把多个小HFile合并生成一个大HFile。因为读出一条完整的行可能引用很多文件,限制HFile的数量对于读性能很重要。执行合并时,HBase读出已有的多个HFile的内容,把记录写入一个新文件。然后,把新文件设置为激活状态,删除构成这个新文件的所有老文件。HBase根据文件的号码和大小决定合并哪些文件。小合并设计出发点是轻微影响HBase的性能,所以涉及的HFile的数量有上限。这些都可以设置。小合并的示意如图4所示。
这里写图片描述
图4 小合并的示意图
大合并将处理给定region的一个列族的所有HFile。大合并完成后,这个列族的所有HFile合并成一个文件。可以从Shell中手工触发整个表(或者特定region)的大合并。这个动作相当耗费资源,不要经常使用。另一方面,小合并是轻量级的,可以频繁发生。大合并是HBase清理被删除记录的唯一机会。因为我们不能保证被删除的记录和墓碑标记记录在一个HFile里面,而大合并可以确保同时访问到两种记录。


本人微信公众号:zhouzxi,请扫描以下二维码:
HBase学习总结(3):HBase的数据模型及工作机制_第1张图片

你可能感兴趣的:(软件项目实践中的SQL语言一瞥)