HBase原理和基本架构和HBase优化(包括RowKey设计)

2022年9月3日10:17:20

数据模型

HBase原理和基本架构和HBase优化(包括RowKey设计)
1)Name Space
命名空间,类似于关系型数据库的database概念,每个命名空间下有多个表。HBase两个自带的命名空间,分别是hbase和default,hbase中存放的是HBase内置的表,default表是用户默认使用的命名空间。
2)Table
类似于关系型数据库的表概念。不同的是,HBase定义表时只需要声明列族即可,不需要声明具体的列。这意味着,往HBase写入数据时,字段可以动态、按需指定。因此,和关系型数据库相比,HBase能够轻松应对字段变更的场景。
3)Row
HBase表中的每行数据都由一个RowKey和多个Column(列)组成,数据是按照RowKey的字典顺序存储的,并且查询数据时只能根据RowKey进行检索,所以RowKey的设计十分重要。
4)Column
HBase中的每个列都由Column Family(列族)和Column Qualifier(列限定符)进行限定,例如info:name,info:age。建表时,只需指明列族,而列限定符无需预先定义。
5)Time Stamp
用于标识数据的不同版本(version),每条数据写入时,系统会自动为其加上该字段,其值为写入HBase的时间。最大的时间戳为新版数据,其它都是旧版数据;并且存储版本数可以通过shell命令更改,默认会存三个版本的数据(并且表示的时候会将所有版本的数据都展示)
6)Cell
由{rowkey, column Family:column Qualifier, time Stamp} 唯一确定的单元。cell中的数据全部是字节码形式存贮。
HBase原理和基本架构和HBase优化(包括RowKey设计)
总结:对于一张表来说,可以通过ROWKey,列族名,列名来锁定一个位置,这个位置通过时间戳的不同又可以放几个版本的单元,新版只有一个,版本的数量由我们设置,每个单元就是一个cell。可以将 HBase模型比喻为图书馆,列族就是哪个书架,ROWKey就是第几排,列名就是第几列。这个位置我们又可以放很多本同样的书。

Region:
而为了方便分布式,我们又将每个ROWKey分区来划分为Region,如果是图书馆的话,就是按每排来划分,注意这里不是按列族来划分的,是按每行ROWKey,如第1005行到1010行,每行可以包括多个列族
Store:
每个Region对应的每个列族叫一个Store,也就是说一个Region可以又多个Store

架构

HBase原理和基本架构和HBase优化(包括RowKey设计)
1)Region Server(每个节点上的一个进程,等下细讲,每个节点一个)
Region Server为 Region的管理者,其实现类为HRegionServer,主要作用如下:
对于数据的操作:get, put, delete;(区别Master,谁操作表,谁操作表的数据)
对于Region的操作:splitRegion、compactRegion。
2)Master(Master是单点启动的,谁先启动的谁是Master。当上一个Master挂了,通过zookeeper可以迅速找到下一个Master,并且即使原Master再次启动,依然不会改变Master)
Master是所有Region Server的管理者,其实现类为HMaster,主要作用如下:
对于表的操作:create, delete, alter
对于RegionServer的操作:分配regions到每个RegionServer,监控每个RegionServer的状态,负载均衡和故障转移。
3)Zookeeper
HBase通过Zookeeper来做master的高可用、RegionServer的监控、元数据的入口(读写请求的时候需要元数据,这些底下讲)以及集群配置的维护等工作。
4)HDFS
HDFS为Hbase提供最终的底层数据存储服务,同时为HBase提供高可用的支持。

HA高可用

至于基于zookeeper的高可用,则是谁先开启master谁就是master,但是没有备用master,此时未开启高可用HA。只需在配置文件修改
HBase原理和基本架构和HBase优化(包括RowKey设计)
配置号之后可以检查
HBase原理和基本架构和HBase优化(包括RowKey设计)
发现又备胎Master,此时关上原先的Master所在节点,发现上不来哦web端,但是转到103的web就可以
HBase原理和基本架构和HBase优化(包括RowKey设计)

HBase 进阶

HBase原理和基本架构和HBase优化(包括RowKey设计)
1)StoreFile
保存实际数据的物理文件,StoreFile以Hfile的形式存储在HDFS上。每个Store会有一个或多个StoreFile(HFile),数据在每个StoreFile中都是有序的。
2)MemStore
写缓存,由于HFile中的数据要求是有序的,所以数据是先存储在MemStore中,排好序后,等到达刷写时机才会刷写到HFile,每次刷写都会形成一个新的HFile。
3)WAL
由于数据要经MemStore排序后才能刷写到HFile,但把数据保存在内存中会有很高的概率导致数据丢失,为了解决这个问题,数据会先写在一个叫做Write-Ahead logfile的文件中,然后再写入MemStore中。所以在系统出现故障的时候,数据可以通过这个日志文件重建。
4)BlockCache
读缓存,每次查询出的数据会缓存在BlockCache中,方便下次查询。

HBase原理和基本架构和HBase优化(包括RowKey设计)
1)Client先访问zookeeper,获取hbase:meta表位于哪个Region Server。
2)访问对应的Region Server,获取hbase:meta表,根据读请求的namespace:table/rowkey,查询出目标数据位于哪个Region Server中的哪个Region中。并将该table的region信息以及meta表的位置信息缓存在客户端的meta cache,方便下次访问。
3)与目标Region Server进行通讯;
4)将数据顺序写入(追加)到WAL;
5)将数据写入对应的MemStore,数据会在MemStore进行排序;
6)向客户端发送ack(注意发送时机,是落盘到一个Hfile就会返回ack,而不是等这个hfile再有备份后在发送,对比kafka的ack);
7)等达到MemStore的刷写时机后,将数据刷写到HFile(注:一个Region的刷写会将所有store都进行刷写操作,即使这个store的Memstore中没有缓存数据)。

HBase原理和基本架构和HBase优化(包括RowKey设计)
MemStore刷写时机:

1.当某个memstroe的大小达到了hbase.hregion.memstore.flush.size(默认值128M),其所在region的所有memstore都会刷写。
当memstore的大小达到了(因为是周期性判断大小的,所以可能出现四倍缓存大小)
hbase.hregion.memstore.flush.size(默认值128M)

  • hbase.hregion.memstore.block.multiplier(默认值4)
    时,会阻止继续往该memstore写数据。(因为它检查是否达到128M并不是一直检查,而是过一会检查一下;如果某次检查完并没有达到128M,刚检查完,数据传输增加,瞬间增加到128的4倍以上,下次检查发现时,就会阻止继续往缓冲区写数据)

2.当region server中memstore的总大小达到
java_heapsize
*hbase.regionserver.global.memstore.size(默认值0.4)
*hbase.regionserver.global.memstore.size.lower.limit(默认值0.95),

注:也就是jvm给region分的内存的百分之四十用来memstore的写流程,假设给region的内存为a,当memstore内存大小达到a * 0.4 * 0.95时;region会按照其所有memstore的大小顺序(由大到小)依次进行刷写。直到region server中所有memstore的总大小减小到上述值以下。

但你避免不了来的数据突然变多,此时有可能达到a0.4的这个界限,此时会阻止继续往所有的memstore写数据。(
也就是说当region server中memstore的总大小达到 java_heapsize
hbase.regionserver.global.memstore.size(默认值0.4)
时,会阻止继续往所有的memstore写数据。)

  1. 到达自动刷写的时间,也会触发memstore flush。自动刷新的时间间隔由该属性进行配置hbase.regionserver.optionalcacheflushinterval(默认1小时)。(对比flume的hdfs sink)

4.当WAL文件的数量超过hbase.regionserver.max.logs,region会按照时间顺序依次进行刷写,直到WAL文件数量减小到hbase.regionserver.max.log以下(该属性名已经废弃,现无需手动设置,最大值为32)。

HBase原理和基本架构和HBase优化(包括RowKey设计)
)Client先访问zookeeper,获取hbase:meta表位于哪个Region Server。
2)访问对应的Region Server,获取hbase:meta表,根据读请求的namespace:table/rowkey,查询出目标数据位于哪个Region Server中的哪个Region中。并将该table的region信息以及meta表的位置信息缓存在客户端的meta cache,方便下次访问。
3)与目标Region Server进行通讯;
4)分别在MemStore和Store File(HFile)中查询目标数据,并将查到的所有数据进行合并。此处所有数据是指同一条数据的不同版本(time stamp)或者不同的类型(Put/Delete)。
5)将查询到的新的数据块(Block,HFile数据存储单元,默认大小为64KB)缓存到Block Cache。
6)将合并后的最终结果返回给客户端。

如何添加一个节点进入HBase

下载好HBase后有两种方法可以添加,第一种是先关上集群,然后在HBase的配置文件的regionservers中加上新加入的节点名,然后再重启集群
第二种方法是基于zookeeper来实现的,只需要配置好该新增加节点的配置文件,让它能连上zookeeper,然后在对它单点启动,即可自动将其加入到HBase集群中

HBase优化(包括RowKey设计)

每一个region维护着startRow与endRowKey,如果加入的数据符合某个region维护的rowKey范围,则该数据交给这个region维护。那么依照这个原则,我们可以将数据所要投放的分区提前大致的规划好,以提高HBase性能。(避免某个region的rowkey太多,其它region的rowkey数量太少,造成数据倾斜)
所以我们一般情况下是在拿到数据后,要提前按照需求来对RowKey进行设计,方便计算

常用的预分区计划

1.手动设定预分区
create ‘staff1’,‘info’, SPLITS => [‘1000’,‘2000’,‘3000’,‘4000’]
2.生成16进制序列预分区
create ‘staff2’,‘info’,{NUMREGIONS => 15, SPLITALGO => ‘HexStringSplit’}
3.按照文件中设置的规则预分区
创建splits.txt文件内容如下:
aaaa
bbbb
cccc
dddd
然后执行:
create ‘staff3’, ‘info’,SPLITS_FILE => ‘splits.txt’
4.使用JavaAPI创建预分区

RowKey设计

1.生成随机数、hash、散列值
2.字符串反转
3.字符串拼接
这样直接说很难想象出来怎么设计RowKey会比较好,所以这里介绍一个例子来方便理解

首先我们能拿到的数据是一列手机号,一列手机号拨打电话的时间戳,一列每个电话队对应的打了多久的时间数
需求: 存储手机号在某个时间打了多少分钟的电话
首先想到的是按照手机的前三位来设计分区,但是有个问题是手机前三位就那几个,并且很容易出现某个前三位如138等数据特别多,再次出现数据倾斜

所以我们直接设定直接用手动设定预分区,直接设置为300个,所以开头分区将会为很小的数值到000,000到001,001到002这样往下。然后我们对手机号进行hash运算,这样尽量将手机号打散,再用打散后的hash值对分区数取模,得到的值作为它所在的区号,并放在rowkey最前面。这时候我们可以想象一下现在的情况,大概是300个分区,假设一共有3000种不同的手机号,那每个分区理想状态下大概分了10个手机号左右(因为hash打乱会将手机号尽量不同)

可这并没有结束,我们可以想想这样一种情况,如果只是对手机号hash打乱后分区,那么每个分区其实打散的其实是手机号,而不是拨打数量,因为同样的一个手机号进入的一定是同一个分区。这时候就有可能出现,某个分区分得的手机号都是经常打电话的手机号,比如打了10w条。另外一个分区分得的手机号都是不经常打电话的,比如10条。那么又会产生数据倾斜,那么我们如何解决比较好呢。
结论是可以通过手机号再相乘时间来进行hash打乱分区。比如如果需求是要统计手机号在某个月打了多少电话这种需求,这样的话我们在rowkey中将手机号和月份连在一起,在搜索rowkey的时候会非常的方便,比如我们要某个手机号10月份的数据,直接搜索rowkey为手机号加10月即可。此时我们在设计的时候就可以通过手机号 * 月份来一起打乱成hash码再来取模,这样就可以将同一个手机号的数据再次打散。

这个时候我们在设计rowkey时要用分区号来拼上时间如上面将的就可以用手机号拼接年月份。但是你止不住会有同时同月且在同一个分区的手机号,所以还要在在最后面拼接上手机号。此时rowkey形式变为
分区号_yyyy-MM-dd HH-mm-ss(根据需求)_手机号

此时举例我们如何往这个表里放数据,同样我们能拿到的数据是一列手机号,一列手机号拨打电话的时间戳,一列每个电话队对应的打了多久的时间数。此时我们需要写代码先将时间戳中的设定时间取出,如年月这一部分取出,然后再用手机号乘它取hash然后算出这条数据所在分区号,拼接上时间,手机号,就完成了这条数据的rowkey。

那我们如何再需要找到某人在十月份的数据,那么只需要算出它的手机号*10的hash值取模的数值得到分区号,这时候我们得到了分区号,时间,手机号,只需要直接筛选rowkey即可

还有值得注意的一点,我们在手动分区的时候要注意尽量在数字后面加上|,原因是比如我们000分区,我们需要一个界限来规划这个分区最后的hash值,所以尽量让000后面加上|(因为|已经是常用符号中hash值最大的了),这样无论你在000后面拼接什么时间,手机号;都会小于000|。避免应该属于000分区的,却因为hash值比000分区的边界大而跑到下一个分区
HBase原理和基本架构和HBase优化(包括RowKey设计)
这里简单的用001到004来演示下web端能查到的region分区效果
HBase原理和基本架构和HBase优化(包括RowKey设计)

  • 作者:黑星bm
  • 原文链接:https://blog.csdn.net/weixin_45425054/article/details/114327424
    更新时间:2022年9月3日10:17:20 ,共 6062 字。