HBase是一款基于HDFS做存储的,Zookeeper做调度的能够存储半结构化与非结构化数据的数据库。它不同于Hive它是一个真正的数据库产品,它的内部基于顺序IO与内存读写,能够非常高效的实现数据的增删改查。
HBase有如下几个部件协调服务:
HMaster:主要负责,给HRegionServer分配Region,HRegionServer的负载均衡。负责HDFS上的垃圾回收。(待讨论)HMaster一般都会配置成双机热备。一个HMaster与一个BackUpHMaster。
Zookeeper:负责协调HBase集群,保证集群中只有一个HMaster、将HRegionServer的在线情况告诉HMaster、存储meta表的的Region的地址信息、存储HBase的元数据信息。
HRegionServer:维护HMaster分配给自己的Region。处理这些Region的IO信息,包括Region的拆分。
Client:访问HBase保存一些消息以提高效率。
HBase表的逻辑结构如下图所示:
总共由以下几个元素构成:
行键:每一行数据由一个行键来标识,我们在查询HBase中的数据时就是通过行键来定位的。一般的查询有三种方式,①根据行键查询,②根据行键范围查询,③全表扫描
列族:列族是一Hbase表从纵向来切分的逻辑结构,一个列族包含若干列。它是Hbase元数据的一部分。
列:一个列族中包含多个列,在创建HBase表的时候不需要创建列因为它可以在表创建之后动态的添加。它不是元数据的一部分。
单元格:由一个行键所标识的行和某一个列族中的某一列相交形成,这是Hbase中存储数据的最小单位。它里面可以存放若干值,他们分别代表这个单元格中的数据的不同版本。其中不同版本的单元格会被时间戳锁标识。需要注意的是Hbase中没有数据类型所有的类型都是二进制。
HBase将一个表从逻辑上横向切分成若干个HRegion,这些HRegion会分布在不同的机器上我们把这些·机器称之为HRegionServer。我们也可以看到HRegion是实现HBase分布式与负载均衡的基础。
起初,一个表中只包含一个HRegion,所有的数据都放在一个HRegion中。但是随着数据量的积累,HRegion中的数据会越来越多,这时HRegion就会产生分裂。以此类推数据不断增大就会不断的分裂产生更多的HRegion。如下图所示:
而HRegion的内部结构如下图所示:
一个HBase表包含着多个HRegion;
一个HRegion中包含着多个Store(Store的数量与表中列族的数量是一致的);
一个Store中又包含了两种组件:
当我们要写入一条数据时
①首先通过表名与行键找到我们需要操作的HRegion(由于是横向切割所以使用的是行键来确定HRegion)。
②然后根据列族找到对应的Store。对该Store中的memStore中写数据。当memStore写数据溢出时就会开启一个新的memStore而旧的memStore会讲之前的数据写到一个storeFile中。最后持久化到HDFS中存储为HFile文件。
③HFile会越存越大然后产生分裂。成不同的小文件。又,memStore的会不断的更新数据,这就使得HFile中的数据过期或者是同一条数据在多个HFile中不一致。这样HFile就会定时触发合并再分裂的机制。具体来说就是将几个HFile合并起来,然后将最新版本的数据覆盖旧版本的数据。最后再将这个文件分裂开来。
写入的过程常常会伴随两个问题:
一个就是memStore写满的问题,这个我们在上面已近做了介绍。还有一个问题就是内存数据断电丢失的问题。解决这个问题的核心组件叫做HLog,每当我们写入一条数据的时候就会提前向HLog中写入一条数据。与此同时,当memStore写满并且开始同步时会向Zookeeper集群发送一个位置信息也叫做redo point。当断电丢失数据时我们首先去找Zookeeper集群然后获得redo point这样我们就会能够定位到HLog中需要恢复的数据了。由于HLog是顺序IO所以即使是基于硬盘的读写但是效率不会太差。最后为了避免太多的HLog文件HBase规定每一个HRegion只能有一个HLog文件。
写入文件的流程如下图所示:
HBase写数据需要用到一些概念,我们来分别介绍。
HBase的寻址过程
给定一个表、一个行键、一个列族、一个列如何查到这个值。这个过程必然会先得到目标Region(也就是对应的HRegionServer)。而寻址就是如何根据上述的信息从而定位到那台HRegion的。在HBase中有一张特殊的表(HBase:Meta表),这个表中存放着所有数据的元信息。也就是通过上面的信息再结合这张元数据的表我们就能够很轻易的找到我们要找的目标HRegion。
但是HBase:Mate本身也是一张表也需要去寻找,但有所不同的是这张表所对应的HRegion的信息会放在Zookeepr集群中。所以,我们找到一个目标HRegion的正确姿势就是:
HFile主要由以下几部分组成:
LSM树的存储策略
LSM树就是一种特殊的B+Tree。它的核心思想就是,将一颗大的数分解成多棵小树,然后等待时机到来时再将小树与大树合并。对应到HBase中就是,在memStore中记录着临时的数据,这些数据按照行键构建成一棵小的B+Tree,随着数据量越来越大当内存中的B+树达到一个阈值时,HBase就会将内存中的小树持久化到硬盘中。然后再将这颗小树与硬盘中已经存在的大树所合并。这样的话会出现内存中的数据与硬盘上的数据不一致的问题,因为这是一个批处理。但是基于搜索树对数级时间复杂度的情况下效率还是非常高的。这就是HBase中的数据存储策略,他的构造如下图所示:
HBase的读取数据流程
现在开始正式聊HBase的读取数据流程,首先树是寻址过程。我们会根据表明,行键、列族找到对应的RegionServer然后通过,找到对应的HRegion,然后根据列族找到对应的Store。首先在memStore中寻找这对应的值,如果找到的话就立即返回。如果没有找到的话就开始在所有的StoreFile中找,首先我们找到对应的Trailer它里面包含着各个其它块的偏移量,这样的话就能够轻易地定位到各个数据段。通过Trailer段找到对应的DataBlocksIndex段,然后首先查看索引中是否存在如果不存在直接返回空,如果存在的话就将对应的DataBlock。
这样的话多个StoreFile会返回多个HFile而这里面可能包含一个数据的不同版本的数据。最后再选择一个版本最高的数据。在硬盘上查数据是要比在内存中查数据慢一个数量级。
这里总结一下HBase的特点:基于内存与顺序IO读写效率高,按照有序的行键进行查找数据底层基于LSM树查找复杂度低,由于最终的数据都在HDFS上面所以能够存储大量的数据而且数据有很好的可靠性。再加上HMaster与BackUpHMaster的双机热备使得主节点更加的可靠。对于稀疏的数据存储效率高,空闲的部分不会占用任何空间。只支持行级别的事务是一款正儿八经的数据库产品。
HBase表的设计
列族!列族!
当一个HBase表设计完成时是很难改变的,标设计的不好效率会大打折扣。我们通常需要考虑到的指标是行键与列族。
行键
行键在HBase中相当于索引所以他的设计也是非常考究的。实务上通常会遵循以下的规则: