一、架构
1、RegionServer
直接对接用户的读写请求,是真正干活的节点。
2、Region
表的分片。
一个RegionServer中可以有多个不同的Region。
一张表中的数据被横向切割存储到一个或多个Region中,每个Region就是一片。小表的话,一个Region就够了,一个Region来自于一张表。
HBase中的表的行会根据RowKey的值存储在不同的Region中。
一张表可以分成多个Region,而Region分布在不同的RegionServer里。
Region的大小可以设置,Region中的数据按照RowKey进行排序,每个Region都有startkey和endkey。
3、Store
Region中的每个列族都是有个Store。
Store包括一个MemStore和多个StoreFile(由MemStore溢出)。
4、Memstore
写缓存。
默认为128M,满128M溢出成StoreFile。
HBase的写操作,首先会将操作写入到HLog,再将数据写入MemStore中,达到指定大小之后再批量写入StoreFile中。
5、BlockCache
读缓存。
HBase在读取数据时,首先会查找MemStore,找不到再找BlockCache,再找不到才到HFile中找。
6、StoreFile
StoreFile是逻辑上的概念,HFile是物理上的概念,StoreFile以HFile的形式存储到HDFS上。一个Store中有多个StoreFile,StoreFile是由MemStore溢写下来的。
7、WAL机制
先将操作顺序写入日志中,然后再将数据写入缓存。WAL可以保证数据在任何情况下都不丢失数据。
8、HLog
一个RegionServer只要一个HLog,所有的Region都写入到同一个HLog。
9、HBase三维有序
RowKey、列族(列)、时间戳
10、RowKey
有序不重复,存储时按照RowKey字典排序。
二、读写流程
1、读流程
1)client向zookeeper集群请求获取元数据(meta表)所在的RegionServer。
2)ZK集群返回元数据表(meta表)所在的RegionServer,例如在RS1 hadoop2节点。
3)client从元数据表所在的RegionServer获取源数据表,并将元数据表进行缓存。
4)client向元数据表所在的RS请求该表Rowkey所在的RS。
5)元数据表所在的RS返回RowKey所在的RS。
6)client向该RS发送读请求。
7)RS返回结果
先从MemStore(写缓存)找再从BlockCache(读缓存)找,再从StoreFile找。
如果是从StoreFile里面读取的数据,不是直接返回给客户端,而是先写入BlockCache,再返回给客户端。
2、写流程
1)client向ZK集群获取元数据表所在的RegionServer
2)ZK集群返回元数据表所在的RegionServer
3)client从元数据表所在的RegionServer获取元数据表,并将元数据表进行缓存。
4)client向RegionServer发送写请求。
5)RegionServer使用WAL(write ahead log)将数据写到HLog。
6)RegionServer将数据写到内存(MemStore)
7)到达128M溢写到StoreFile
8)RegionServer返回给client写成功。
三、RowKey的设计
RowKey可以唯一标识一行记录。
1.长度原则
建议越短越好,不超过16个字节。
2.散列原则
在前缀加随机数使数据均匀的分布到不同的RegionServer。
注:一个表的数据可以分布到多个Region中,而多个Region可以分布在多个RegionServer里。
3.唯一原则
保证RowKey的唯一。
四、热点问题
1、什么是热点问题?
HBase中的行是按照RowKey的字典顺序排序的,这种设计优化了scan操作,可以将相关的行以及会被一起读取的行存储在临近位置,便于scan。然而糟糕的RowKey设计是热点的源头。
热点发生在大量的client直接访问集群的一个或极少数个节点(访问可能是读、写),大量的访问会使热点Region所在的单个机器超出自身承受能力,引起性能下降甚至region不可能用,这也会影响同一个RegionServer上的其他region,而设计良好的RowKey可以使集群被均匀的利用。
2、如何避免热点问题?
为了避免热点问题,数据应该被写入集群的多个Region,而不是一个。
首先要设计RowKey,然后设计预分区。
3、常见的设计RowKey的手段
1)加盐
给RowKey加上随机前缀。
2)哈希
即给RowKey加上Hash码前缀,并通过Hash码重构,进行查找。
五、预分区
1、为什么要预分区
HBase在创建表的时候,会自动为表分配一个Region,但是实际生产环境中,创建新表后,我们往往是需要往这个Region里导入大量的数据,之后大量的操作就同时集中在了一个Region上,有了热点问题,而且,Region达到一定的阈值就会split,原始只有一个Region,却导入这么多的数据,这样会导致大量的split操作,RegionServer可能会出现问题。
如何解决这个问题呢?那就是创建表的时候我们就多创建一个Region,这就是预分区。
2、创建表时设置预分区
通过SPLIT或SPLIT_FILE可以在创建表的时候指定分区。
如果分区数很多,写在命令行就不合适了,那么就写在文件里。
通过webUI查看我们对应的Region信息,如下图:
有3个Region,可是我们不是创建时,只写了3个吗?就是4个,指定x,最终会有x+1个,第一个没有StartKey,最后一个没有EndKey。
3、如何最好的设置预分区
合理的RowKey设计,如随机散列,与预分区二者结合起来,是比较完美的,预分区一开始就预建好了一部分Region,这些Region都维护着自己的start_end keys,再配合上随机散列,写数据能均匀写在这些预建的Region,就能够解决上面的热点问题。