文章作者邮箱:[email protected] 地址:广东惠州
⚪ 掌握Hbase的基本架构;
⚪ 掌握Hbase的读写流程;
⚪ 掌握Hbase的设计与优化;
1. 在HBase中,会将一个表从行键方向上进行切分,切分成1个或者多个HRegion。
2. 切分之后,每一个HRegion都会交给某一个HRegionServer来进行管理。
3. 一个表至少会包含1个HRegion,可以包含多个HRegion。
4. 在HBase中,行键是有序的,因此从行键方向上来进行的切分,所以HRegion之间的数据是不交叉的。
5. 因为HRegionServer会交给HRegionServer来管理,并且HRegion之间的数据相互不交叉,所以保证请求不会集中于某一个节点上而是会分散到不同的节点上。
6. 随着运行时间的推移,HRegion管理的数据会不断增多,达到指定条件的时候,会自动的进行分裂。
7. 每一个HRegion包含1个到多个HStore,HStore的数量由列族的数量来决定。
8. 每一个HStore都会包含1个memStore以及包含0到多个StoreFile/HFile。
1. 在HBase中,Zookeeper充当了注册中心。
2. 当HBase启动之后,会自动的在Zookeeper上来注册一个/hbase节点。
3. 当Active HMaster启动之后,会自动的在Zookeeper上注册一个临时节点/hbase/master - 当Active HMaster宕机之后,这个临时节点就会消失,此时Zookeeper就会从Backup HMasters中选择最早注册的节点来切换为Active状态。
4. 当Backup HMaster启动之后,会自动的在Zookeeper的/hbase/backup-masters节点上注册一个临时子节点。
5. 当HRegionServer启动之后,也会自动的在Zookeeper的/hbase/rs节点下来注册子节点。
1. 在HBase中,允许用户在任意一台安装了HBase的节点上来启动HMaster,理论上不限制HMaster的数量。
2. HMaster启动命令:
hbase-daemon.sh start master
3. 在HBase中,如果启动了多个HMaster,那么HMaster之间就会分为Active和Backup两种状态。
4. 如果启动多个HMaster,那么最先注册到Zookeeper上的HMaster就会成为Active状态,后注册到Zookeeper上的HMaster就会成为Backup状态。
5. 当Active HMaster接收到请求之后,需要考虑将数据同步给其他的Backup HMasters。同步的节点数量越多,此时效率就会越低。
6. 因此在HBase中,虽然理论上不限制HMaster的个数,但是实际过程中,HMaster的个数一般不会超过3个:1个Active HMaster+2个Backup HMasters。
7. Active HMaster会实时监控Zookeeper上/hbase/backup-masters下的节点变化以确定需要同步的节点是哪几个。
8. HMaster的作用:
a. 管理HRegionServer。需要注意的是,HMaster对HRegionServer的管理权限并不大,只能决定HRegion交由哪一个HRegionServer来进行管理。
b. 记录和存储元数据。HBase中的元数据包含namespace名、table名、column family名以及属性信息等。注意,在HBase中,列不是元数据,因为列可以动态增删 - 也就意味着凡是产生元数据的操作会经过HMaster,不产生元数据的操作不会经过HMaster。DDL(Data Defination Language,数据定义语言,例如create/drop等)以及namespace操作会产生元数据,DML(Data Manipulation Language,数据操纵定义,例如put/get/scan/delete等)语言不会产生元数据。
1. 当客户端要进行DML操作的时候,会首先发送请求到Zookeeper,请求获取hbase:meta表的位置,这个表中存储HBase的元数据。
2. Zookeeper收到请求之后,会将hbase:meta表的位置返回给客户端。hbase:meta会由某一个HRegionServer管理。
3. 客户端收到hbase:meta表的位置之后,会请求对应的HRegionServer,来读取hbase:meta,从这个表中获取到实际要操作的HRegion所在的位置。
4. 客户端获取到HRegion的实际所在位置之后,会再次发送请求给对应的HRegionServer,来操作这个HRegion。
5. 注意问题:
a. 当客户端第一次请求Zookeeper之后,会自动缓存hbase:meta文件的位置,之后客户端的每次请求就可以不用再访问Zookeeper。
b. 当客户端获取到HRegion的位置之后,也会自动缓存这个HRegion的位置,之后如果还操作这个HRegion,就可以直接访问。
c. 随着时间的推移,客户端缓存的位置越来越多,此时效率就会越来越高。但是如果客户端发生宕机,那么此时会导致缓存崩溃,那么需要重新建立缓存。
1. 在实际生产过程中,一般会考虑将HRegionServer和DataNode部署在相同的节点上,避免频繁的跨集群的请求。
2. HRegionServer的作用是用于管理HRegion。官方文档中给定,每一个HRegionServer大概能够管理1000个HRegion。每一个HRegion默认能够最多管理10G数据。
3. 每一个HRegionServer包含三部分结构:1到多个WAL,1个BlockCache以及0到多个HRegion。
a. WAL(Write Ahead Log):发生写操作之前的日志。
Ⅰ. 当HRegionServer接收到写请求之后,会先试图将请求记录到WAL中,之后再将数据更新到对应的memStore中。
Ⅱ. 通过WAL这个机制,能够有效的保证数据不会产生丢失,但是因为WAL是落地在磁盘上的,因此会导致写入效率在一定程度上会降低。因此在实际过程中,如果容忍一定程度上的数据丢失的风险而想要提高写入效率,那么此时可以考虑关闭WAL机制。
Ⅲ. 在HBase0.94版本之前,WAL只能采用串行写机制;从HBase0.94版本开始,引入了NIO中的Channel机制,使得WAL支持使用并行写机制,从而保证效率能够提升。
b. BlockCache:数据块缓存。
Ⅰ. BlockCache本质上是一个读缓存,维系在内存中,默认大小是128M。
Ⅱ. 在HBase中,在读取数据的时候,会将读取到的数据放到BlockCache中,从而下次再次读取数据的时候,可以从BlockCache中获取,减少对HStore的读取。
Ⅲ. BlockCache在缓存的时候,还采用了"局部性"原理。所谓的"局部性"原理本质上就是一个猜测的过程,无非是利用时间或者空间条件来合理猜测以提高命中率。
1. 时间局部性:在HBase中,如果一条数据被读取过,那么HBase会认为这条数据被再次的概率要高于其他的没有被读取过的数据,那么此时HBase就会将这条数据放到缓存中 - 只要是读取过的数据就会放到缓存中。
2. 空间局部性:在HBase中,如果一条数据被读取过,那么HBase会认为与这条数据相邻的数据被读取的概率要高于其他的不相邻的数据,那么此时HBase就会将与这条数据相邻的数据也放到缓存中。
Ⅳ. 随着时间的推移,BlockCache会被放满,那么此时BlockCache就会采用LRU(Least Recently Used,最近最少使用的数据就会被清理掉)策略。
c. HRegion:HBase分布式存储和管理的基本结构,但不是数据存储的最小单位。
Ⅰ. 每一个HRegion会至少包含1个HStore,可以包含多个HStore,HStore的数量由列族的数量来决定。
Ⅱ. 每一个HStore中会包含1个memStore以及0到多个HFile/StoreFile。
Ⅲ. memStore本质上是一个写缓存,维系在内存中,大小默认是128M,可以通过hbase.hregion.max.filesize属性来调节。
Ⅳ. 当达到一定条件的时候,就会将memStore进行冲刷,冲刷产生HFile。HFile最终会以Block形式落地到DataNode上。
Ⅴ. memStore的冲刷条件。
1. 当memStore被用满之后,会自动的进行冲刷,产生一个HFile。
2. 当距离上一次冲刷达到指定的时间间隔(默认是1H - 3600000ms,可以通过属性hbase.regionservers.optionalcacheflushinterval来修改,注意单位是毫秒)之后,也会自动的冲刷memStore产生HFile。
3. 当某一个HRegionServer上所有的memStore所占内存之和/实际物理内存>0.4,那么会冲刷当前HRegionServer上较大的几个memStore,直到这个值小于0.4为止。
Ⅵ. 随着运行时间的推移,第三个条件更容易满足,此时会冲刷产生大量的小文件。
1. 在HBase中,提供了2种Compaction机制:minor compact和major compact。
a. minor compact:初次合并。在合并的时候,会将当前HStore中相邻的几个小的HFile合并成一个大的HFile,原本就是大的HFile不参与合并,因此合并完成之后依然存在多个HFile。
b. major compact:主要合并。在合并的时候,会将当前HStore中所有的HFile进行合并,因此合并完成之后只存在一个HFile。
2. 相对而言,minor compact合并效率更高一些,HBase中默认采用的合并机制也是minor comapct。实际过程中,也会使用major compact,但是因为major compact的效率较低,需要对大量数据进行读写,因此一般是放在相对空闲的时间来进行。
3. 需要注意的是,在major compact的时候,会自动清理掉被标记为删除的数据或者过时的数据。
1. 当HRegionServer接收到写请求的时候,会先将这个写请求记录到WAL中,记录成功之后会再将数据更新到memStore中。
2. 数据在memStore中会进行排序,按照行键字典序->列族字典序->列字典序->时间戳倒序来进行排序。
3. 当达到冲刷条件的时候,memStore会自动冲刷产生HFile。因为memStore中的数据已经排序,所以冲刷出来的单个HFile中的数据是有序的。所有的HFile之间是局部有序整体无序的。
4. HFile最终会以Block形式落地到HDFS的DataNode上。
5. HFile的v1版本的结构:
a. DataBlock:数据块。用于存储数据:
Ⅰ. 每一个HFile中包含1个到多个DataBlock,DataBlock是数据存储的基本结构/最小单位。
Ⅱ. 因为HFile中的数据是有序的,所以切分出来的DataBlock之间的数据是不交叉的。
Ⅲ. 每一个DataBlock大小默认是64KB。小的DataBlock利于查询(get),大的DataBlock利于遍历(scan)。
Ⅳ. 每一个DataBlock都是由1个Magic(魔数)以及1到多个KeyValue来构成。
1. Magic:魔数。本质上就是一个随机数,用于校验的。
2. KeyValue:存储数据,每一条数据最终都会以键值对形式来进行存储。
b. MetaBlock:元数据块。用于存储元数据的。注意,不是所有的HFile都包含这一部分,一般只有hbase:meta表对应的HFile会包含这一部分。
c. FileInfo:文件信息。用于记录HFile大小、所属HStore等信息。
d. DataIndex:数据索引,用于记录DataBlock的索引。
e. MetaIndex:元数据索引,用于记录MetaBlock的索引。
f. Trailer:在文件末尾,占用固定的字节大小,用于记录FileInfo,DataIndex和MetaIndex在文件中的起始字节。
6. 在HFile中,需要先读取文件末尾,通过Trailer来锁定DataIndex的位置,然后读取DataIndex,通过DataIndex来定位DataBlock的位置。
7. 在HFile的v2版本中,引入了BloomFilter(布隆过滤器)。
1. 当HRegionServer结束到读请求的时候,会先考虑从BlockCache中来获取数据。
2. 如果BlockCache中没有数据,那么会试图从memStore中来获取。
3. 如果memStore中也没有数据,那么会试图从HFile中来获取。在读取HFile的时候,可以先根据行键范围进行筛选,筛选掉不符合范围的HFile,但是不代表剩余的HFile中一定有找的数据。筛选完成之后,如果开启了布隆过滤器,那么可以利用布隆过滤器再次筛选,被筛选掉的文件中一定没有要找的数据,但是不代表剩余的文件中有要找的数据。
1. 行键设计:
a. 行键在设计的时候要尽量的散列,例如可以考虑使用哈希、加密算法等使结果散列,这样能保证请求不会集中于一个节点上。
b. 行键设计最好有意义,如果行键真的完全随机,会增加查询难度,例如订单的行键可以设计为:210510abj025 -> 520jba015012。
c. 行键在使用的时候要保证唯一。
2. 列族设计:
a. 在HBase中虽然理论上不限制列族的数量,但是实际过程中,一个表中的列族数量一般不会超过3个。
b. 在设计列族的时候,要尽量将具有相同特性的数据或者经常一起使用的数据放在一个列族中,尽量避免跨列族查询。
1. 调节DataBlock的大小。小的DataBlock利于查询,大的DataBlock利于遍历。在建表的时候,就可以根据当前场景来确定DataBlock的大小。例如:
create 'person', {NAME => 'basic', BLOCKSIZE = '32768'}
2. 关闭BlockCache。如果HBase的遍历偏多,此时没有必要将数据放到读缓存中,此时可以考虑关闭BlockCache。
create 'person', {NAME => 'basic', BLOCKCACHE => 'false'}
alter 'person', {NAME => 'basic', BLOCKCACHE => 'false'}
3. 更改BloomFilter的级别。BloomFilter支持三种方式:NONE,ROW以及ROWCOL。NONE不使用BloomFilter,如果节点硬件性能一般,可以考虑关闭BloomFilter;ROW对行键进行过滤,BloomFilter默认就是这个值;ROWCOL表示对行键、列族和列同时过滤,如果节点硬件性能较好,可以使用这个值。
4. 开启数据压缩机制。如果HBase占用了大量的HDFS空间,导致HDFS空间不够,那么可以考虑对HBase的数据进行压缩。通过COMPRESSION属性来修改,支持NONE,LZO,SNAPPY和GZIP。其中NONE表示不压缩,HBase默认不对数据压缩。
5. 在查询的时候可以考虑显式地指定列,此时可以减少在网络中传输的数据量。例如:
get 'person', 'p1'
get 'person', 'p1', 'basic'
get 'person', 'p1', 'basic:name'
6. 如果数据量较大,那么在读写的时候可以考虑使用批量读写。
7. 关闭WAL。如果想要提高写入效率,又能够容忍一定的数据丢失,那么可以考虑关闭WAL。
8. 预创建HRegion。当HRegion管理的数据比较多(默认是10G)的时候,会进行分裂。HRegion分裂之后可能会发生管理权的转移,此时HRegion的分裂和转移都要花费时间。因此在能够预估数据量的前提下,可以考虑在建表的时候就构建多个HRegion。例如:
hbase org.apache.hadoop.hbase.util.RegionSplitter person HexStringSplit -c 15 -f basic
9. 调整Zookeeper的有效Session时长。默认情况下,HMaster和Zookeeper之间通过心跳来保证联系,心跳间隔时间默认是180s即3min,也就意味着HMaster每隔3min会给Zookeeper发送一次心跳。如果HMaster产生了故障,那么可能Zookeeper需要在3min之后才能发现故障。在业务高峰期,HBase有3min不能使用,此时会造成大量的损失。因此需要调节这个时长。通过属性zookeeper.session.timeout来调节,单位是秒,放在hbase-site.xml中。