Day49_深入HBase(一)

(一)重要工作机制

1、读数据流程

  • 从zookeeper找到meta表所在的region的位置,然后读取meta表中的数据。而meta中又存储了用户表的region信息

ZK:/hbase/meta-region-server,该节点保存了meta表的region server数据

  • 根据namespace、表名和rowkey根据meta表中的数据找到对应的region信息

scan "hbase:meta", { FILTER => "PrefixFilter('ORDER_INFO')"}

ROW                                      COLUMN+CELL
ORDER_INFO                              column=table:state, timestamp=1622254701537, value=\x08\x00
ORDER_INFO,,1622254700267.d65e1e7a6a321 column=info:regioninfo, timestamp=1622373881754, value={ENCODED => d65e1e7a6a32104b50bae2e688e8a151, NAME => 'ORDER_IN
04b50bae2e688e8a151.                    FO,,1622254700267.d65e1e7a6a32104b50bae2e688e8a151.', STARTKEY => '', ENDKEY => ''}
ORDER_INFO,,1622254700267.d65e1e7a6a321 column=info:seqnumDuringOpen, timestamp=1622373881754, value=\x00\x00\x00\x00\x00\x00\x02_
04b50bae2e688e8a151.
ORDER_INFO,,1622254700267.d65e1e7a6a321 column=info:server, timestamp=1622373881754, value=bd-offcn-02:16020
04b50bae2e688e8a151.
ORDER_INFO,,1622254700267.d65e1e7a6a321 column=info:serverstartcode, timestamp=1622373881754, value=1622373861799
04b50bae2e688e8a151.
ORDER_INFO,,1622254700267.d65e1e7a6a321 column=info:sn, timestamp=1622373880034, value=bd-offcn-02,16020,1622373861799
04b50bae2e688e8a151.
ORDER_INFO,,1622254700267.d65e1e7a6a321 column=info:state, timestamp=1622373881754, value=OPEN
04b50bae2e688e8a151.
  • 找到对应的regionserver,查找对应的region

 

  • 从MemStore找数据,再去BlockCache中找,如果没有,再到StoreFile上读
  • 可以把MemStore理解为一级缓存,BlockCache为二级缓存,但注意scan的时候BlockCache意义不大,因为scan是顺序扫描

2、数据存储流程

  1. HBase V2.x以前版本
  • 写内存(MemStore)
  • 二阶段StoreFiles合并

2、Vx

  • In-memory compaction(带合并的写内存)
  • 二阶段StoreFiles合并

HBase的数据存储过程是分为几个阶段的。写入的过程与HBase的LSM结构对应。

  1. 为了提高HBase的写入速度,数据都是先写入到MemStore(内存)结构中,V2.0 MemStore也会进行Compaction
  2. MemStore写到一定程度(默认128M),由后台程序将MemStore的内容flush刷写到HDFS中的StoreFile
  3. 数据量较大时,会产生很多的StoreFile。这样对高效读取不利,HBase会将这些小的StoreFile合并,一般3-10个文件合并成一个更大的StoreFile

(1)写入MemStore

  • Client访问zookeeper,从ZK中找到meta表的region位置
  • 读取meta表中的数据,根据namespace、表名、rowkey获取对应的Region信息
  • 通过刚刚获取的地址访问对应的RegionServer,拿到对应的表存储的RegionServer
  • 去表所在的RegionServer进行数据的添加
  • 查找对应的region,在region中寻找列族,先向MemStore中写入数据

(2)MemStore溢写合并

 

a、说明

  •  当MemStore写入的值变多,触发溢写操作(flush),进行文件的溢写,成为一个StoreFile
  •  当溢写的文件过多时,会触发文件的合并(Compact)操作,合并有两种方式(major,minor)

b、触发条件

Region级别

<property>
<name>hbase.hregion.memstore.flush.size</name>
<value>134217728</value>
<source>hbase-default.xml</source>
</property>

当一个 Region 中所有 MemStore 占用的内存达到了上限hbase.hregion.memstore.flush.size,默认128MB,会触发Memstore刷新。

但是如果数据增加得很快,达到了hbase.hregion.memstore.flush.size * hbase.hregion.memstore.block.multiplier的大小,

hbase.hregion.memstore.block.multiplier 默认值为4,也就是128*4=512MB的时候,那么除了触发 MemStore 刷写之外,HBase 还会在刷写的时候同时阻塞所有写入该 Store 的写请求!这时候如果你往对应的 Store 写数据,会出现RegionTooBusyException 异常

  • RegionServer级别

HBase 为 RegionServer 的整体的 MemStore 分配了一定的写缓存,大概占用 RegionServer 整个 JVM 内存使用量的 40%

hbase.regionserver.global.memstore.size=0.4,如果整个 RegionServer 的所有 MemStore 占用内存总和大于写缓存的0.95。将会触发 MemStore 的刷写。

hbase.regionserver.global.memstore.size.lower.limit=0.95

举个例子,如果我们 HBase 堆内存总共是 32G,按照默认的比例,那么触发 RegionServer 级别的 Flush 是 RS 中所有的 MemStore 占用内存为:32 * 0.4 * 0.95 = 12.16G。

RegionServer 级别的Flush策略是按照Memstore由大到小执行,先Flush Memstore最大的Region,再执行次大的,直至总体Memstore内存使用量低于阈值。需要注意的是,如果达到了RegionServer 级别的 Flush,那么当前 RegionServer 的所有写操作将会被阻塞而且这个阻塞可能会持续到分钟级别

  • 自动刷写
<property>
<name>hbase.regionserver.optionalcacheflushinterval</name>
<value>3600000</value>
<source>hbase-default.xml</source>
</property>
  • 手动刷写

用户可以通过shell命令 flush ‘tablename’或者flush ‘region name’分别对一个表或者一个Region进行flush。

disable 'user'
drop 'user'
create 'user', 'info', 'data'

put 'user','001','info:name','zhangsan'
flush 'user'

put 'user','002','info:name','lisi'
flush 'user'

 (3)模拟数据查看MemStore使用情况

注意:此处小数是无法显示的,只显示整数位的MB。

  •  将之前的水费账单数据再次导入一遍,查看效果。

hbase org.apache.hadoop.hbase.mapreduce.Import WATER_BILL /water_bill/output_ept_10W

  • 打开Region所在的Region Server

 点击Memory查看内存占用情况

 将数据可以手动刷新到到磁盘

hbase(main):004:0> flush 'WATER_BILL'

(4) In-memory合并(了解)

a、In-memory compaction介绍

In-memory合并是HBase 2.0之后添加的。它与默认的MemStore的区别:实现了在内存中进行compaction(合并)

在CompactingMemStore中,数据是以段(Segment)为单位存储数据的。MemStore包含了多个segment。

  • 当数据写入时,首先写入到的是Active segment中(也就是当前可以写入的segment段)
  • 在2.0之前,如果MemStore中的数据量达到指定的阈值时,就会将数据flush到磁盘中的一个StoreFile
  • 2.0的In-memory compaction,active segment满了后将数据移动到pipeline中。这个过程跟以前不一样,以前是flush到磁盘,而这次是将Active segment的数据,移到称为pipeline的内存当中。一个pipeline中可以有多个segment。而In-memory compaction会将pipeline的多个segment合并为更大的、更紧凑的segment,这就是compaction
  • HBase会尽量延长CompactingMemStore的生命周期,以达到减少总的IO开销。当需要把CompactingMemStore flush到磁盘时,pipeline中所有的segment会被移动到一个snapshot中,然后进行合并后写入到HFile

 

b、compaction策略

但Active segment flush到pipeline中后,后台会触发一个任务来合并pipeline中的数据。合并任务会扫描pipeline中所有的segment,将segment的索引合并为一个索引。有三种合并策略:

  • basic(基础型)
    • Basic compaction策略不清理多余的数据版本,无需对cell的内存进行考核(检查是否版本多余)
    • basic适用于大量写模式
  • eager饥渴型
    • eager compaction会过滤重复的数据,清理多余的版本,这会带来额外的开销
    • eager模式主要针对数据大量过期淘汰的场景,例如:购物车、消息队列等
  • adaptive(适应型)
    • adaptive compaction根据数据的重复情况来决定是否使用eager策略
    • 该策略会找出cell个数最多的一个,然后计算一个比例,如果比例超出阈值,则使用eager策略,否则使用basic策略

c、配置

  • 可以通过hbase-site.xml来配置默认In Memory Compaction方式
<property>

    <name>hbase.hregion.compacting.memstore.type</name>

    <value><none|basic|eager|adaptive></value>

</property>



hbase-2.2.4版本

<property>

    <name>hbase.systemtables.compacting.memstore.type</name>

    <value> NONE | BASIC | EAGER ></value>

</property>
  • 在创建表的时候指定
create "test_memory_compaction", {NAME => 'C1', IN_MEMORY_COMPACTION => "BASIC"}

(5)StoreFile合并

  1.  当MemStore超过阀值的时候,就要flush到HDFS上生成一个StoreFile。因此随着不断写入,HFile的数量将会越来越多,根据前面所述,StoreFile数量过多会降低读性能
  2.  为了避免对读性能的影响,需要对这些StoreFile进行compact操作,把多个HFile合并成一个HFile

 compact操作需要对HBase的数据进行多次的重新读写,因此这个过程会产生大量的IO。可以看到compact操作的本质就是以IO操作换取后续的读性能的提高

a、minor compaction

(1)说明

  • Minor Compaction操作只用来做部分文件的合并操作,不做任何删除过期数据、多版本数据的清理工作
  • 小范围合并,默认是3-10个文件进行合并,不会删除其他版本的数据
  • Minor Compaction则只会选择数个StoreFile文件compact为一个StoreFile
  • Minor Compaction的过程一般较快,而且IO相对较低

(2)触发条件

  •  在打开Region或者MemStore时会自动检测是否需要进行Compact(包括Minor、Major)
  •  minFilesToCompact由hbase.hstore.compaction.min控制,默认值为3
  •  即Store下面的StoreFile数量减去正在compaction的数量 >=3时,需要做compaction

可以查看具体配置:

http://bd-offcn-01:16010/conf

<property>
<name>hbase.hstore.compaction.min</name>
<value>3</value>
<final>false</final>
<source>hbase-default.xml</source>
</property>

b、major compaction

(1)说明

  • Major Compaction操作是对Region下的Store下的所有StoreFile执行合并操作,会物理上清理掉过期数据、被用户删除的数据,多余版本数据,最终的结果是整理合并出一个文件
  • 一般手动触发(自动触发每隔7天),会删除其他版本的数据(不同时间戳的)

(2)触发条件

  • 如果无需进行Minor compaction,HBase会继续判断是否需要执行Major Compaction

将Store下面所有StoreFile合并为一个StoreFile,此操作会删除其他版本的数据(不同时间戳的),忽略标记为delete的KeyValue(被删除的KeyValue只有在compact过程中才真正被"删除"),可以想象major会产生大量的IO操作,对HBase的读写性能产生影响。

major 大合并 默认7天进行一次 生成环境中建议将其关闭 由自己在业务空闲的时候手动触发,因为大合并的时候会非常消耗IO资源。

hbase.hregion.majorcompaction = 建议设置为0,还可以设置抖动比例(hbase.hregion.majorcompaction.jitter),默认是0.5,最终的周一起为3.5-10.5天

<property>
<name>hbase.hregion.majorcompaction</name>
<value>604800000</value>
<source>hbase-default.xml</source>
</property>

604800000毫秒 = 604800秒 = 168小时 = 7天

手动触发方式:触发完成之后,可以查看hdfs的数据,原本多个storefile文件会变成一个。

手动触发方式:触发完成之后,可以查看hdfs的数据,原本多个storefile文件会变成一个。

hbase(main):009:0> major_compact 'WATER_BILL'

3、Region管理

(1)region分配

  • 任何时刻,一个region只能分配给一个region server
  • Master记录了当前有哪些可用的region server,以及当前哪些region分配给了哪些region server,哪些region还没有分配。当需要分配的新的region,并且有一个region server上有可用空间时,master就给这个region server发送一个装载请求,把region分配给这个region server。region server得到请求后,就开始对此region提供服务。

(2)region server上线

  • Master使用ZooKeeper来跟踪region server状态
  • 当某个region server启动时
    • 首先在zookeeper上的/hbase/rs目录下建立代表自己的znode
    • 由于Master订阅了/hbase/rs目录上的变更消息,当/hbase/rs目录下的文件出现新增或删除操作时,master可以得到来自zookeeper的实时通知
    • 一旦region server上线,master能马上得到消息。

(3)region server下线

  • 当region server下线时,它和zookeeper的会话断开,ZooKeeper而自动释放代表这台server的文件上的独占锁
  • Master就可以确定
    • region server和zookeeper之间的网络断开了
    • region server挂了
  • 无论哪种情况,region server都无法继续为它的region提供服务了,此时master会删除server目录下代表这台region server的znode数据,并将这台region server的region分配给其它还活着的节点

(4)Region分裂

当region中的数据逐渐变大之后,达到某一个阈值,会进行裂变

  1. 一个region等分为两个region,并分配到不同的RegionServer
  2. 原本的Region会下线,新Split出来的两个Region会被HMaster分配到相应的HRegionServer上,使得原先1个Region的压力得以分流到2个Region上。
<-- Region最大文件大小为10G -->

<property>

<name>hbase.hregion.max.filesize</name>

<value>10737418240</value>

<final>false</final>

<source>hbase-default.xml</source>

</property>
  • HBase只是增加数据,所有的更新和删除操作,都是在Compact阶段做的
  •  用户写操作只需要进入到内存即可立即返回,从而保证I/O高性能读写

a、自动分区

自动分区的算法 根据hbase版本的不同而不同,可以引入hbase-server包查看源码

hbase0.94版本以前是:ConstantSizeRegionSplitPolicy

0.94-2.0.0是:IncreasingToUpperBoundRegionSplitPolicy

2.0.0及以后是:SteppingSplitPolicy

<dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-server</artifactId>
            <version>2.2.4</version>
        </dependency>

 0.94以前如果一个region大小超过10G才会进行切片

0.94-2.0.0之间,如果一个表中region的数量等于0或者大于100,则按照10G切片

否则,每次计算一个表中region的数量,然后通过region数*region数*region数*2*flushsize(即128M)得出一个值,将该值与10G比较,取最小值,

某个region的大小超过该最小值才切片,

例如,

此时region数量为2,则2*2*2*256=2048M,该值小于10G,则这两个region中的一个超过2048M,则可切片。

此时region数量为3,则3*3*3*256=6912M,该值小于10G,则这3个region中的一个超过6912M,则可切片。

此时region数量为4,则4*4*4*256=16384M,该值大于10G,则这4个region中的一个超过10G,则可切片。

即可总结,若一个表的region数量大于等于4的话,是按照10G切片

b、手动分区

在创建表的时候,就可以指定表分为多少个Region。默认一开始的时候系统会只向一个RegionServer写数据,系统不指定startRow和endRow,可以在运行的时候提前Split,提高并发写入。

hbase(main):012:0> create 'staff1','info',SPLITS => ['1000','2000','3000','4000']

4、Master工作机制

(1)Master上线

Master启动进行以下步骤:

  1. 从zookeeper上获取唯一一个代表active master的锁,用来阻止其它master成为master
  2. 一般hbase集群中总是有一个master在提供服务,还有一个以上的‘master’在等待时机抢占它的位置。
  3. 扫描zookeeper上的/hbase/rs父节点,获得当前可用的region server列表
  4. 和每个region server通信,获得当前已分配的region和region server的对应关系
  5. 扫描meta表,计算得到当前还未分配的region,将他们放入待分配region列表

(2)Master下线

  • 由于master只维护表和region的元数据,而不参与表数据IO的过程,master下线仅导致所有元数据的修改被冻结
    1. 无法创建删除表
    2. 无法修改表的schema
    3. 无法进行region的负载均衡
    4. 无法处理region 上下线
    5. 唯一例外的是region的split可以正常进行,因为只有region server参与,但是新分裂出的region没法交给master进行分配给其它regionserver维护
    6. 表的数据读写还可以正常进行
  • 因此master下线短时间内对整个hbase集群没有影响
  • 从上线过程可以看到,master保存的信息全是可以冗余信息(都可以从系统其它地方收集到或者计算出来)

(二) HBase批量装载(Bulk load)

1、简介

很多时候,我们需要将外部的数据导入到HBase集群中,例如:将一些历史的数据导入到HBase做备份。我们之前已经学习了HBase的Java API,通过put方式可以将数据写入到HBase中,我们也学习过通过MapReduce编写代码将HDFS中的数据导入到HBase。但这些方式都是基于HBase的原生API方式进行操作的。这些方式有一个共同点,就是需要与HBase连接,然后进行操作。HBase服务器要维护、管理这些连接,以及接受来自客户端的操作,会给HBase的存储、计算、网络资源造成较大消耗。此时,在需要将海量数据写入到HBase时,通过Bulk load(大容量加载)的方式,会变得更高效。可以这么说,进行大量数据操作,Bulk load是必不可少的。

我们知道,HBase的数据最终是需要持久化到HDFS。HDFS是一个文件系统,那么数据可定是以一定的格式存储到里面的。例如:Hive我们可以以ORC、Parquet等方式存储。而HBase也有自己的数据格式,那就是HFile。Bulk Load就是直接将数据写入到StoreFile(HFile)中,从而绕开与HBase的交互,HFile生成后,直接一次性建立与HBase的关联即可。使用BulkLoad,绕过了Write to WAL,Write to MemStore及Flush to disk的过程

更多可以参考官方对Bulk load的描述:

Apache HBase ™ Reference Guide

2、Bulk load MapReduce程序开发

Bulk load的流程主要分为两步:

  1. 通过MapReduce准备好数据文件(Store Files)
  2. 加载数据文件到HBase

3、银行转账记录海量数据存储案例

银行每天都产生大量的转账记录,超过一定时期的数据,需要定期进行备份存储。本案例,在MySQL中有大量转账记录数据,需要将这些数据保存到HBase中。因为数据量非常庞大,所以采用的是Bulk Load方式来加载数据。

  • 项目组为了方便数据备份,每天都会将对应的转账记录导出为CSV文本文件,并上传到HDFS。我们需要做的就将HDFS上的文件导入到HBase中。
  • 因为我们只需要将数据读取出来,然后生成对应的Store File文件。所以,我们编写的MapReduce程序,只有Mapper,而没有Reducer。

(1)数据集

id

ID

code

流水单号

rec_account

收款账户

rec_bank_name

收款银行

rec_name

收款人姓名

pay_account

付款账户

pay_name

付款人姓名

pay_comments

转账附言

pay_channel

转账渠道

pay_way

转账方式

status

转账状态

timestamp

转账时间

money

转账金额

(2)项目准备工作

a、HBase中创建银行转账记录表

create_namespace "OFFCN_BANK"
# disable "TRANSFER_RECORD"
# drop "TRANSFER_RECORD"
create "OFFCN_BANK:TRANSFER_RECORD", { NAME => "C1", COMPRESSION => "GZ"}, { NUMREGIONS => 6, SPLITALGO => "HexStringSplit"}

b、创建项目

groupid

com.offcn

artifactid

bankrecord_bulkload

c、导入POM依赖

<dependencies>
   <dependency>
        <groupId>org.apache.hbase</groupId>
        <artifactId>hbase-client</artifactId>
        <version>2.2.4</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hbase</groupId>
        <artifactId>hbase-mapreduce</artifactId>
        <version>2.2.4</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-mapreduce-client-jobclient</artifactId>
        <version>3.2.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-common</artifactId>
        <version>3.2.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-mapreduce-client-core</artifactId>
        <version>3.2.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-auth</artifactId>
        <version>3.2.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-hdfs</artifactId>
        <version>3.2.1</version>
    </dependency>
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.6</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

d、创建包结构

说明

com.offcn.bank_record.bulkload.mr

MapReduce相关代码

com.offcn.bank_record.entity

实体类

e、导入配置文件

将 core-site.xml、hbase-site.xml、log4j.properties三个配置文件拷贝到resources目录中。

(3)编写实体类

实现步骤:

  1. 创建实体类TransferRecord
  2. 添加一个parse静态方法,用来将逗号分隔的字段,解析为实体类
  3. 使用以下数据测试解析是否成功

7e59c946-b1c6-4b04-a60a-f69c7a9ef0d6,SU8sXYiQgJi8,6225681772493291,杭州银行,丁杰,4896117668090896,卑文彬,老婆,节日快乐,电脑客户端,电子银行转账,转账完成,2020-5-13 21:06:92,11659.0

参考代码:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值