HBase架构及各节点的作用
-
HMaster的作用:维护表和region的元数据,对表模式的操作,为HRegionServer分配Region
负责管理HBase元数据,即表的结构、表存储的Region等元信息。
负责表的创建,删除和修改(因为这些操作会导致HBase元数据的变动)。
负责为HRegionServer分配Region,分配好后也会将元数据写入相应位置(后面会详细讲述放在哪)。 -
HRegionServer作用:
处理Client端的读写请求(根据从HMaster返回的元数据找到对应的Region来读写数据)。
管理Region的分裂、StoreFile的Compaction合并。
HBase读写
参考文献:
[1] HBase系列(二):HBase架构及读写流程
[2] HBase:数据读写基本流程
- 写
Client访问ZK,根据ROOT表获取meta表所在Region的位置信息,并将该位置信息写入Client Cache。
(注:为了加快数据访问速度,我们将元数据、Region位置等信息缓存在Client Cache中。)
.
Client读取meta表,再根据meta表中查询得到的Namespace、表名和RowKey等相关信息,获取将要写入Region的位置信息(此过程即Region三层定位,如下图),最后client端会将meta表写入Client Cache。
.
Client向上一步HRegionServer发出写请求,HRegionServer先将操作和数据写入HLog(预写日志,Write Ahead Log,WAL),再将数据写入MemStore,并保持有序。
(联想:HDFS中也是如此,EditLog写入时机也是在真实读写之前发生)
.
当MemStore的数据量超过阈值时,将数据溢写磁盘,生成一个StoreFile文件。
当Store中StoreFile的数量超过阈值时,将若干小StoreFile合并(Compact)为一个大StoreFile。
当Region中最大Store的大小超过阈值时,Region分裂(Split),等分成两个子Region。 - 读
1,Client先访问zookeeper,从meta表读取region的位置,然后读取meta表中的数据。meta中又存储了用户表的region信息。
2,根据namespace、表名和rowkey在meta表中找到对应的region信息
3,找到这个region对应的regionserver
4,查找对应的region
5,先从MemStore找数据,如果没有,再到StoreFile上读(为了读取的效率)。 - 读 简版:
获取将要读取Region的位置信息(同读的1、2步)。
Client向HRegionServer发出读请求。
HRegionServer先从MemStore读取数据,如未找到,再从StoreFile中读取。
1 HBase预分区
1.1 为什么要进行预分区?
为了解决“热点问题”。
热点问题:在HBase中查询数据时,需要通过RowKey定位数据行。当大量客户端访问少数几个结点,造成少数RegionServer的读写请求过多,而其他RegionServer负载却很小,就造成了“热点”现象。
热点问题出现的原因:
hbase的中的数据行,按照行键的字典序排序,当大量连续的rowkey集中写在个别的region,各个region之间数据分布不均衡;
1.2 怎么进行预分区?
通过设计RowKey,进行预分区。
1.2.1 常用方法
参考自:hbase热点问题解决(预分区)
-
第一种设计rowkey方式:随机数+messageId,如果想让最近的数据快速get到,可以将时间戳加上。
-
第二种设计rowkey的方式:通过messageId映射regionNo,这样既可以让数据均匀分布到各个region中,同时可以根据startkey和endkey可以get到同一批数据
messageId映射regionNo,使用一致性hash算法解决。
什么是一致性hash算法?参考:五分钟理解一致性哈希算法(consistent hashing)
1.2.2 大部分方法
下面是一些常见的避免热点的方法以及它们的优缺点:参考自Hbase中的rowkey以及热点问题、阿里云-RowKey表设计
-
加盐
这里所说的加盐不是密码学中的加盐,而是在rowkey的前面增加随机数,具体就是给rowkey分配一个随机前缀以使得它和之前的rowkey的开头不同。给多少个前缀?这个数量应该和我们想要分散数据到不同的region的数量一致(类似hive里面的分桶)。加盐之后的rowkey就会根据随机生成的前缀分散到各个region上,以避免热点。 -
哈希
哈希会使同一行永远用一个前缀加盐。哈希也可以使负载分散到整个集群,但是读却是可以预测的。使用确定的哈希可以让客户端重构完整的rowkey,可以使用get操作准确获取某一个行数据。 -
反转
第三种防止热点的方法是反转固定长度或者数字格式的rowkey。这样可以使得rowkey中经常改变的部分(最没有意义的部分)放在前面。这样可以有效的随机rowkey,但是牺牲了rowkey的有序性。
.
反转rowkey的例子:以手机号为rowkey,可以将手机号反转后的字符串作为rowkey,从而避免诸如139、158之类的固定号码开头导致的热点问题。 -
时间戳反转
一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为rowkey的一部分对这个问题十分有用,可以用Long.Max_Value - timestamp追加到key的末尾,例如[key][reverse_timestamp] ,[key] 的最新值可以通过scan [key]获得[key]的第一条记录,因为HBase中rowkey是有序的,第一条记录是最后录入的数据。 -
尽量减少行和列的大小
在HBase中,value永远和它的key一起传输的。当具体的值在系统间传输时,它的rowkey,列名,时间戳也会一起传输。如果你的rowkey和列名很大,HBase storefiles中的索引(有助于随机访问)会占据HBase分配的大量内存,因为具体的值和它的key很大。可以增加block大小使得storefiles索引再更大的时间间隔增加,或者修改表的模式以减小rowkey和列名的大小。压缩也有助于更大的索引。 -
其他办法
列族名的长度尽可能小,最好是只有一个字符。冗长的属性名虽然可读性好,但是更短的属性名存储在HBase中会更好。也可以在建表时预估数据规模,预留region数量,例如create 'myspace:mytable’, SPLITS => [01,02,03,…99]