Hbase读写数据流程
regionserver
HRegionServer内部管理了一系列HRegion对象,每个HRegion对应了Table中的一个Region,HRegion中由多个HStore组成,每个HStore对应了Table中的一个Column Family的存储,
可以看出每个Column Family其实就是一个集中的存储单元,因此最好将具备共同IO特性的column放在一个Column Family中,这样最高效
HStore存储由两部分组成,一部分是MemStore(内存),一部分是StoreFiles(磁盘)
容错与恢复
每个HRegionServer中都有一个HLog对象,HLog是一个实现Write Ahead Log的类,在每次用户操作写入MemStore的同时,也会写一份数据到HLog文件中,HLog文件定期会滚动出新的,并删除旧的文件(已持久化到StoreFile中的数据)
WAL就像日志中心一样,它被同一个region server中的所有region共享。
当客户端启动一个操作来修改数据,该操作便会被封装成一个KeyValue对象实例中,并通过RPC调用发送出去。
这些调用成批的发送给含有匹配region的HRegionServer。一旦KeyValue到达,它们会被发送到管理相应行的HRegion对象实例。数据便被写入到WAL,然后放入到实际拥有记录的存储文件的MemStore中。
最后当memstore达到一定的大小或者经历一个特定时间之后,数据就会异步地连续写入到文件系统中。
在写入的过程中,数据以一种不稳定的状态存放在内容中,即使在服务器完全崩溃的情况下,WAL也能够保证数据不丢失,因为实际的日志存储在HDFS上。其他服务器可以打开日志然后回放这些修改。
当HRegionServer意外终止后,HMaster会通过Zookeeper感知到,HMaster首先会处理遗留的HLog文件,将其中不同Region的Log数据进行拆分,分别放到相应region的目录下,然后再将失效的region重新分配,领取到这些region的HRegionServer在LoadRegion的过程中,会发现有历史HLog需要处理,因此会ReplayHLog中的数据到MemStore中,然后flush到StoreFiles,完成数据恢复
每个Region Server维护一个Hlog,而不是每个Region一个。
这样做的目的是不断追加单个文件相对于同时写多个文件而言,可以减少磁盘寻址次数,因此可以提高对table的写性能。
带来的麻烦是,如果一台Region Server下线,为了恢复其上的Region,需要将Region Server上的log进行拆分,然后分发到其它Region Server上进行恢复。
1 客户端寻找HRegionServer,及缓存位置信息
从这个过程中,我们发现客户会缓存这些位置信息,然而第二步它只是缓存当前RowKey对应的HRegion的位置,因而如果下一个要查的RowKey不在同一个HRegion中,则需要继续查询hbase:meta所在的HRegion,然而随着时间的推移,客户端缓存的位置信息越来越多,以至于不需要再次查找hbase:meta Table的信息,除非某个HRegion因为宕机或Split被移动,此时需要重新查询并且更新缓存。
2 写数据流程
1)通过2.3.1 找到该写数据最终需要去的HRegionServer;
2)然后客户端将写请求发送给相应的HRegionServer,在HRegionServer中它首先会将该写操作写入WAL(Hlog)日志文件中(Flush到磁盘中)。
3)写完WAL日志文件后,HRegionServer根据Put中的TableName和RowKey,startkey、endkey找到对应的HRegion,并根据Column Family找到对应的HStore,并将Put写入到该HStore的MemStore中。此时写成功,并返回通知客户端。
写入MemStore后的操作:
存入MemStore,一直到MemStore满
→ Flush成一个StoreFile,直至增长到一定阈值
→ 触发Compact合并操作 -> 多个StoreFile合并成一个StoreFile,同时进行版本合并和数据删除
→ 当StoreFiles Compact后,逐步形成越来越大的StoreFile
→单个StoreFile大小超过一定阈值(Region split 阈值)后,触发Split操作,把当前Region Split成2个Region,Region会下线,新Split出的2个子Region会被HMaster分配到相应的HRegionServer上,使得原先1个Region的压力得以分流到2个Region上;
什么时候执行MemStore Flush?
1)当一个MemStore的大小超过了hbase.hregion.memstore.flush.size的大小,此时当前的HRegion中所有的MemStore会Flush到HDFS中,不阻塞写操作。(Region级别的flush)
2)当全局MemStore的大小超过了hbase.regionserver.global.memstore.upperLimit(默认0.4,memstores所占最大堆空间比例)的大小,此时当前HRegionServer中所有HRegion中的MemStore都会Flush到HDFS中,阻塞写操作。(RS级别的flush)
3)当一个Region的MemStore总量达到hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size(默认2*128M=256M)时,会阻塞这个region的写操作,并强制刷写到HDFS。
触发这个刷新只会发生在MemStore即将写满128M时put了一个巨大的记录的情况,这时会阻塞写操作,强制刷新成功才能继续写入。(Region级别的flush)
4)当前HRegionServer中HLog的大小超过阈值,当前HRegionServer中所有HRegion中的MemStore都会Flush到HDFS中。(RS级别的flush)
3 读数据流程
1)通过2.3.1 找到要读的数据的HRegionServer。
2)根据读取的TableName和RowKey的startkey 、 endkey 找到对应的HRegion。
4)每个regionserver只有一个blockcache(读缓存),读取数据时,先到memestore上读数据,找不到再到blockcahce上找数据,再查不到则到磁盘(storefile)查找,并把读入的数据同时放入blockcache。
4 Region拆分策略
Region概念
一个Table由一个或多个Region组成
一个Region由一个或者多个Store组成,每个store保存一个columns family;
一个Strore又由一个memStore和0至多个StoreFile 组成。memStore存储在内存中, StoreFile存储在HDFS上。
Region大小考量的因素
1)机器个数多,Region个数少,浪费机器,region数目太少就会妨碍可扩展性,降低并行能力,导致压力不够分散;
2)region小,数目太多就会造成性能下降;
Region拆分策略
HRegionServer拆分region的步骤是,先将该region下线,然后拆分,将其子region加入到hbase:meta表中,再将他们加入到原本的HRegionServer中,最后汇报Master。
split前
hbase:meta: region_p
split中
region_p ---> region1
region2
在 region_p 对应的hdfs目录下生成.splits 目录, 里面创建 两个子region的引用文件
tablename/region_p_md5/.splits/region1 引用文件
/region2 引用文件
在表目录下创建子region对应的hdfs目录, 把引用文件拷贝到 指定region目录下
tablename/region1_md5/region1 引用文件
/2region_md5/region2 引用文件
split后
把子region往hbase:meta表里写
hbase:meta
region_p
region1
region2
两个子region从父region获取数据,当数据获取完,删除相应的引用文件
汇报hmaster
可以通过设置RegionSplitPolicy的实现类来指定拆分策略,RegionSplitPolicy类的实现类有:
ConstantSizeRegionSplitPolicy
IncreasingToUpperBoundRegionSplitPolicy
DelimitedKeyPrefixRegionSplitPolicy
KeyPrefixRegionSplitPolicy
其中:
ConstantSizeRegionSplitPolicy
仅当region大小超过常量值(hbase.hregion.max.filesize大小,默认为10G)时,才进行拆分。
IncreasingToUpperBoundRegionSplitPolicy
默认region split策略。即当同一table在同一regionserver上的region数量在[0,100)之间时按照如下的计算公式算,否则按照上一策略计算:
Min (R^3* "hbase.hregion.memstore.flush.size"*2, "hbase.hregion.max.filesize")
R为同一个table中在同一个regionserver中region的个数,
hbase.hregion.memstore.flush.size默认为128M,
hbase.hregion.max.filesize默认为10G。
第一次分裂: 1*1*1*128*2 = 256 --> 1 --> 2
第二次分裂: 2*2*2*128*2 = 2048 --> 2 --> 4
第三次分裂 : 4 * 4 * 4 * 128 * 2
一个表的region 越分越多 --> hmaster --> 平衡 --> RS --> region 移动(慢)
预分 region 表 数据 --> 够大 -- 分region
表 --> 分出region --> rowkey(范围识别) --> hive的分区 先分区 再存储 数据
DelimitedKeyPrefixRegionSplitPolicy
保证以分隔符前面的前缀为splitPoint,保证相同RowKey前缀的数据在一个Region中。如果定义rowkey时,采用'_'作为字段分隔符(如:userid_eventid),则采用该策略拆分之后,能够确保具有相同userid的记录隶属于同一Region。
假设分割字段是 “_”
userid_eventid --> userid
userid_aaaid
username_aaa ---> username
username_bb
KeyPrefixRegionSplitPolicy
保证具有相同前缀的row在一个region中(要求设计中前缀具有同样长度)。指定rowkey前缀位数划分region,通过读取table的prefix_split_key_policy.prefix_length属性,该属性为数字类型,表示前缀长度,在进行split时,按此长度对splitPoint进行截取。
此种策略比较适合固定前缀的rowkey。当table中没有设置该属性,或其属性不为Integer类型时,指定此策略效果等同与使用IncreasingToUpperBoundRegionSplitPolicy。
假设 prefix_split_key_policy.prefix_length=9
userid001_aaa --> userid001
userid001_bbb
userid002_aaa --> userid002
userid002_bbb
配置拆分策略
hbase配置文件中定义全局的拆分策略,设置hbase.regionserver.region.split.policy;也可以在创建和修改表时候指定。
如果想关闭自动拆分改为手动拆分,建议同时修改hbase.hregion.max.filesize和hbase.regionserver.region.split.policy值。
如果关闭自动拆分策略需要把 hbase.hregion.max.filesize 设置非常大,再采用手动。
爱吃芝麻 发布了107 篇原创文章 · 获赞 0 · 访问量 1240 私信 关注