HBase原理深入

时间:2022-09-11 11:21:16

HBase 读写数据流程

Hbase 读数据流程

HBase原理深入

  1. 首先从 zk 找到 meta 表的 region 位置,然后读取 meta 表中的数据,meta 表中存储了用户表的 region 信息

  2. 根据要查询的 namespace、表名和 rowkey 信息,找到写入数据对应的 region 信息

  3. 找到这个 region 对应的 regionServer,然后发送请求

  4. 查找对应的 region

  5. 先从 Memstore 查找数据,如果没有,再从 BlockCache 上读取。

    Hbase 上 RegionServer 的内存分为两部分:

    • 一部分作为 Memstore,主要用来写
    • 另一部分作为 BlockCache,主要用来读数据
  6. 如果 BlockCache 中也没有,再到 StoreFile 上进行读取。从 StoreFile 中读到数据后,不是直接把结果返回给客户端,而是先把数据写入 BlockCache,然后再返回给客户端。

HBase 写数据流程

HBase原理深入

  1. 首先从 zk 找到 meta 表的 region 位置,然后读取 meta 表中的数据,meta 表中存储了用户表的 region 信息。
  2. 根据 namespace、表名和 rowkey 等信息,找到写入数据对应的 region
  3. 找到这个 region 对应的 regionServer,然后发送写入请求
  4. 把数据分别写入 HLog(Write ahead log)和 memStore
  5. memStore 达到阈值后把数据刷到磁盘,生成 storeFile
  6. 删除 HLog 中的历史数据

HBase 的 flush(刷写)和 compact(合并)机制

Flush 机制

  1. 当 memstore 的大小超过这个值的时候,会 flush 到磁盘,默认为 128M
<property>
<name>hbase.hregion.memstore.flush.size</name>
<value>134217728</value>
</property>
  1. 当 memstore 的数据超过 1 小时,会 flush 到磁盘
<property>
<name>hbase.regionserver.optionalcacheflushinterval</name>
<value>3600000</value>
</property
  1. HregionServer 的全局 memstore 的大小,超过该大小会触发 flush 到磁盘的操作,默认是堆大小的 40%
<property>
<name>hbase.regionserver.global.memstore.size</name>
<value>0.4</value>
</property>
  1. 手动 flush
flush tableName

阻塞机制

以上介绍了数据刷写磁盘的标准,但是 HBase 是周期性的检查是否满足来进行刷写的,如果在下次检查到来之前,数据疯狂写入 Memstore,就会把内存撑爆。那怎么处理这种问题呢?

HBase 有阻塞机制,如果触发,就无法继续写入数据。

  1. 当 Memstore 数据达到 512MB

计算公式:hbase.hregion.memstore.flush.size*hbase.hregion.memstore..block.multiplier

  • hbase.hregion.memstore.flush.size 刷写的阀值,默认是 134217728,即 128MB。
  • hbase.hregion.memstore.block.multiplier 是一个倍数,默认是 4。
  1. RegionServer 全部 memstore 达到规定值

有时候集群的“写负载”非常高,写入量一直超过 flush 的量,这时,我们就希望 memstore 不要超过一定的安全设置。在这种情况下,写操作就要被阻塞一直到 memstore 恢复到一个“可管理”的大小, 这个大小就是默认值是堆大小 _ 0.4 _ 0.95。

  • hbase.regionserver.global.memstore.size.lower.limit 是 0.95
  • hbase.regionserver.global.memstore.size 是 0.4

Compact 合并机制

在 HBase 中主要存在两种类型的 compact 合并。

  1. Minor compact 小合并

将 Store 中的多个 HFile(StoreFile)合并为一个 HFile

这个过程中,删除和更新的数据仅仅只是做了标记,并没有物理移除,这种合并的触发频率很高。

minor compact 文件选择标准由以下几个参数共同决定:

<!--待合并文件数据必须大于等于下面这个值-->
<property>
  <name>hbase.hstore.compaction.min</name>
  <value>3</value>
</property>
<!--待合并文件数据必须小于等于下面这个值-->
<property>
  <name>hbase.hstore.compaction.max</name>
  <value>10</value>
</property>
<!--默认值为128m,
表示文件大小小于该值的store file 一定会加入到minor compaction的store file中
-->
<property>
  <name>hbase.hstore.compaction.min.size</name>
  <value>134217728</value>
</property>
<!--默认值为LONG.MAX_VALUE,
表示文件大小大于该值的store file 一定会被minor compaction排除-->
<property>
  <name>hbase.hstore.compaction.max.size</name>
  <value>9223372036854775807</value>
</property>

触发条件

  • memstore flush

在进行 memstore flush 前后会进行判断是否触发 compact

  • 定期检查线程

周期性检查是否需要进行 compaction 操作,周期性时间由参数:hbase.server.thread.wakefrequency 决定,默认值是 10000 millseconds。

  1. major compact 大合并

合并 Store 中所有的 HFile 为一个 HFile。

这个过程中有删除标记的数据会真正被移除,同时超过单元格 maxVersion 的版本记录也会被删除。合并频率比较低,默认 7 天执行一次,并且性能消耗非常大。建议生产关闭。在应用空闲时间手动触发。这样可以防止出现在业务高峰期进行 compact

触发条件

<!--默认值为7天进行一次大合并,-->
<property>
  <name>hbase.hregion.majorcompaction</name>
  <value>604800000</value>
</property>

手动触发

major_compact tableName

Region 拆分机制

当 Region 中存储的是大量的 rowkey 数据,当 Region 中的数据条数过多的时候,直接影响查询效率。当 Region 过大的时候,HBase 就会拆分 Region。

HBase原理深入

拆分策略

HBase 的 Region Split 策略一共有以下几种:

  1. ConstantSizeRegionSplitPolicy
  • 0.94 版本前默认切分策略

当 region 大小大于某个阈值(hbase.hregion.max.filesize=10G)之后就会触发切分,一个 region 等分为 2 个 region。

但是在生产线上这种切分策略却有相当大的弊端:切分策略对于大表和小表没有明显的区分。阈值(hbase.hregion.max.filesize)设置较大对大表比较友好,但是小表就有可能不会触
发分裂,极端情况下可能就 1 个,这对业务来说并不是什么好事。如果设置较小则对小表友好,但一个大表就会在整个集群产生大量的 region,这对于集群的管理、资源使用、failover 来
说都不是一件好事

  1. IncreasingToUpperBoundRegionSplitPolicy
  • 0.94 版本~2.0 版本默认切分策略

切分策略稍微有点复杂,总体看和 ConstantSizeRegionSplitPolicy 思路相同,一个 region 大小大于设置阈值就会触发切分。但是这个阈值并不像 ConstantSizeRegionSplitPolicy 是一个固定的值,而是会在一定条件下不断调整,调整规则和 region 所属表在当前 regionserver 上的 region 个数有关系.

region split 的计算公式是:

regioncount^3 _ 128M _ 2,当 region 达到该 size 的时候进行 split:

例如::

第一次 split:1^3 _ 256 = 256MB:

第二次 split:2^3 _ 256 = 2048MB:

第三次 split:3^3 _ 256 = 6912MB:

第四次 split:4^3 _ 256 = 16384MB > 10GB,因此取较小的值 10GB:

后面每次 split 的 size 都是 10GB 了

  1. SteppingSplitPolicy
  • 2.0 版本默认切分策略

这种切分策略的切分阈值又发生了变化,相比 IncreasingToUpperBoundRegionSplitPolicy 简单了一些,依然和待分裂 region 所属表在当前 regionserver 上的 region 个数有关系,如果 region 个数等于 1,切分阈值为 flushsize*2,否则为 MaxRegionFileSize。
这种切分策略对于大集群中的大表、小表会比 IncreasingToUpperBoundRegionSplitPolicy 更加友好,小表不会再产生大量的小 region,而是适可而止。

  1. KeyPrefixRegionSplitPolicy

根据 rowKey 的前缀对数据进行分组,这里是指定 rowKey 的前多少位作为前缀,比如 rowKey 都是 16 位的,指定前 5 位是前缀,那么前 5 位相同的 rowKey 在进行 regionsplit 的时候会分到相同的 region 中。

  1. DelimitedKeyPrefixRegionSplitPolicy

保证相同前缀的数据在同一个 region 中,例如 rowKey 的格式为:userideventtype_eventid,指定的 delimiter 为,则 split 的的时候会确保 userid 相同的数据在同一个 region 中

  1. DisabledRegionSplitPolicy

不启用自动拆分, 需要指定手动拆分

拆分策略的应用

Region 拆分策略可以全局统一配置,也可以为单独的表指定拆分策略

  1. 通过 hbase-site.xml 全局统一配置,也可以为单独的表指定拆分策略
<property>
  <name>hbase.regionserver.region.split.policy</name>
  <value>org.apache.hadoop.hbase.regionserver.IncreasingToUpperBoundRegionSplitPolicy</value>
</property>
  1. 通过 Java API 为单独的表指定 Region 拆分策略
HTableDescriptor tableDesc = new HTableDescriptor("test1");
tableDesc.setValue(HTableDescriptor.SPLIT_POLICY, IncreasingToUpperBoundRegionSplitPolicy.class.getName());
tableDesc.addFamily(new HColumnDescriptor(Bytes.toBytes("cf1")));
admin.createTable(tableDesc);
  1. 通过 HBase Shell 为单个表指定 Region 拆分策略
> create 'test2', {METADATA => {'SPLIT_POLICY' =>
'org.apache.hadoop.hbase.regionserver.IncreasingToUpperBoundRegionSplitPolicy'}},{NAME => 'cf1'}

HBase 表的预分区

  1. 为什么要预分区?

当一个 table 刚被创建的时候,HBase 默认分配一个 region 给 table。也就是说这个时候,所有的读写请求都会访问同一个 regionServer 的同一个 region 中,这个时候就达不到负载均衡的效果了,集群中其他的 regionServer 就可能会处于比较空闲的状态。解决这个问题可以用 pre-splitting。在创建 table 的时候就配置好,生成多个 region。

好处就是:

  • 增加数据读写效率
  • 负载均衡,防止数据倾斜
  • 方便集群容灾调度 region

每个 region 维护着 startRow 与 endRowyKey,如果加入的数据符合某个 region 维护的 rowkey 范围,则该数据交给这个 region 来维护。

  1. 手动指定预分区

create 'person','info1','info2',SPLITS => ['1000','2000','3000']

也可以把分区规则创建于文件中。

create 'student','info',SPLITS_FILE => '/root/hbase/split.txt'

Region 合并

Region 的合并不是为了性能,而是出于维护的目的。

Region 合并的方式:

  1. 通过 Merge 类冷合并

需要先关闭 HBase 集群。

不需要进入 hbase shell,直接执行:

合并的信息可以从页面上获取
HBase原理深入

hbase org.apache.hadoop.hbase.util.Merge user user,,1662823434957.f971c62e76cdff90ea957c0709099bb5. user,1000,1662823434957.366fc3c5e6237a8993cc0e143109c229.
  1. 通过 online_merge 热合并 Region

不需要关闭 HBase 集群,在线进行合并

与冷合并不同的是,online_merge 的传参是 Region 的 hash 值,Region 的 hash 值就是 Region 名称的最后那段在两个“.”之间的字符串部分。
HBase原理深入

示例:

merge_region 'c8d1a1b7f709dfcd8b0c574b4121fdca','1d7e67e13a48b67d2d7867ca9717183c'