HBase
Compaction(压实)机制
- 在实际过程中,由于memStore的flush条件的问题,所以容易产生大量的小文件落地到HDFS上。因此HBase针对这个问题,提供了compaction机制
- 所谓的compaction机制,本质上就是将小文件进行merge(合并)操作
- 在HBase中,提供了两种合并机制
- minor compaction:初次紧缩。将相邻的一些小文件合并成一个大文件,如果本身就是一个大文件,则不参与合并。所以合并完成之后,HStore中依然存在多个HFile
- major compaction:主要紧缩。无论文件大小,全部合并,因此合并完成之后,HStore中只存在1个HFile
- 相对而言,major compaction会涉及到大量数据的读写,所以效率相对较低。HBase默认采用的是minor compaction
- 需要注意的是:major compaction过程中,会舍弃掉被标记为删除或者过时的数据
HRegionServer读写流程
写流程
-
当HRegionServer接收到写命令之后,会先将这个命令记录到WAL中,记录成功之后会将数据更新到memStore中
-
数据在memStore中会进行排序,按照行键字典序 -> (列族字典序) -> 列字典序 -> 时间戳倒序的顺序排序
-
当memStore达到条件的时候,会将数据冲刷到HFile中。因为数据在memStore中是有序的,所以冲刷出来的HFile中的数据也是有序的。但是多次冲刷之后,多个HFile之间的数据是局部有序的
-
HFile的v1格式包含了六部分:DataBlock、MetaBlock、DataIndex、MetaIndex、FileInfo和Trailer
-
DataBlock:数据块,用于存储数据的
-
一个HFile中会包含1到多个DataBlock
-
因为HFile中的数据是有序的,所以DataBlock之间的数据也是不交叉的
-
DataBlock大小默认是64KB。小的DataBlock利于查询(get),大的DataBlock利于扫描(scan)
-
BlockCache中的空间局部性就是以DataBlock为单位来缓存:当DataBlock中某一条数据被读取的时候,这个DataBlock就会被放入BlockCache中
-
每一个DataBlock都是由1个Magic以及多个KeyValue来构成的
-
Magic:魔数,本质上就是一个随机数,用于来进行校验的。当生成Block的时候,会伴随生成Magic,如果数据发生变化,Magic也会随机变化
-
KeyValue:键值对。用于存储数据的,而数据最终都是以键值对形式来存储
-
-
MetaBlock:元数据块,用于存储元数据的。不是每一个HFile中都会包含MetaBlock,一般是
hbase:meta
文件中会包含MetaBlock-
FileInfo:文件信息,用于描述HFile的文件信息,例如文件大小等
-
DataIndex:数据索引,记录DataBlock的索引位置
-
MetaIndex:元数据索引,记录MetaBlock的索引位置
-
Trailer:在文件末尾占用固定字节大小,记录DataIndex、MetaIndex和FileInfo在文件的起始位置的
-
-
在HFile中读取数据的时候,需要先通过Trailer来锁定DataIndex的位置,通过DataIndex确定DataBlock的位置,最后从DataBlock中来读取对应的数据
-
在HFile的v2版本中,引入了BloomFilter(布隆过滤器)
- BloomFilter在使用的时候,需要定义一个字节数组和三个不同的哈希函数。针对同一个值利用三个哈希函数来进行计算,计算之后将结果映射到字节数组的某一个位置上
- 当获取数据的时候,可以利用BloomFilter来进行校验,如果计算出的结果为0,那么说明这个数据一定不存在!如果映射到的值全部是1,说明这个数据有可能存在 - BloomFilter只能确定数据不存在,但是不能保证数据存在!
- 随着添加到元素越来越多,那么此时数组中的空位会越来越少,此时误判率会越来越高,解决方案:数组扩容
读流程
- 当HRegionServer接收到读请求的时候,会先试图从BlockCache中来读取数据
- 如果BlockCache中没有指定的数据,那么会试图从memStore中来读取数据
- 如果memStore中也没有指定的数据,那么会试图从StoreFile中来读取数据。在读取StoreFile的时候,会先根据行键范围来筛选掉不符合范围的StoreFile。再利用布隆过滤器来筛选掉不符合的HFile。筛选完成之后,被过滤掉的HFile中一定不包含要找的数据,但是剩余的HFile中不一定包含要找的数据
设计与优化
设计原则
- 行键设计原则
- 行键在设计的时候要尽量散列,避免请求过于集中到某一个节点上,实现请求的负载均衡。例如可以对行键计算哈希值之后再进行存储
- 行键设计最好有意义,例如
video_xxx
,log_xxx
等。但是行键有意义,就意味着行键可能会密集分布,所以可以考虑将行键翻转存储,还可以考虑在行键之前来添加随机值 - 行键必须唯一
- 列族设计原则
- 在HBase中,虽然理论上不限制列族的数量,但是实际过程中,列族的数量最好不超过3个
- 在添加数据的时候,尽量将具有相同性质的列,或者经常查询的列放到同一个列族中,尽量避免跨列族查询
优化
-
调节DataBlock的大小。大的DataBlock利于扫描,小的DataBlock利于查询,所以可以根据实际使用场景来确定DataBlock的大小调节
# 建表的时候来指定DataBlock的大小 create 'test', { NAME => 'a', BLOCKSIZE => '65536'} # 修改DataBlock的大小 alter 'test', { NAME => 'a', BLOCKSIZE => '65536'}
-
关闭BlockCache。如果表被扫描的次数更多,此时BlockCache的作用就不大。因此可以考虑关闭BlockCache
create 'test', { NA