菜鸟先飞之Hive与Hbase

一、Hive与Hbase的集成

1.1Hbase与Hive的对比

1、Hive

(1)数据仓库

Hive 的本质其实就相当于将 HDFS 中已经存储的文件在 Mysql 中做了一个双射关系,以方便使用 HQL 去管理查询。

(2)用于数据分析、清洗

Hive适用于离线的数据分析和清洗,延迟较高

(3)基于HDFS、MapReduce

Hive 存储的数据依旧在 DataNode 上,编写的 HQL 语句终将是转换为MapReduce 代码执行。

2、Hbase

(1)数据库是一种面向列存储的非关系型数据库。

(2)用于存储结构化和非结构化的数据适用于单表非关系型数据的存储,不适合做关联查询,类似 JOIN 等操作。 

(3)基于HDFS数据持久化存储的体现形式是 HFile,存放于 DataNode 中,被 ResionServer以 Region 的形式进行管理。

(4)延迟较低,接入在线业务使用面对大量的企业数据,HBase 可以直线单表大量数据的存储,同时提供了高效的数据访问速度。 

1.2HBase 与 Hive 集成使用(开发重点)

1.案例一 

目标:建立 Hive 表,关联 HBase 表,插入数据到 Hive 表的同时能够影响 HBase表。 

分步实现: 

(1)在 Hive 中创建表同时关联 HBase

ps:-- Hive 中只支持 select 和 insert,不支持 HBase 中的版本控制,在hive中创建的外部表的字段要与Hbase中的表名、列簇:字段名一一对应

-- 在 hive 中创建外部表 
create external table customer( 
name string, 
order_numb string,
order_date string,
addr_city string,
addr_state string) 
stored by 'org.apache.hadoop.hive.hbase.HBaseStorageHandler' 
with serdeproperties 
("hbase.columns.mapping"=":key,order:numb,order:date,addr:city,addr:state") 
tblproperties("hbase.table.name" = "customer");

(2)向 hive 表中插入数据,在 hive 中执行如下语句。 

 insert into table customer values ('James','1121','2018-05-31','toronto','ON'); 

(3)在 HBase Shell 中查看表中的记录。

scan 'customer'

(4) 可以在 HBase 中插入数据,然后在 Hive 表中查看更新的数据。 

select * from customer;

 2.案例二

目标:在 HBase 中已经存储了某一张表 customer,然后在 Hive 中创建一个外部表来关联 HBase 中的 customer 这张表,使之可以借助 Hive 来分析 HBase 这张表中的数据。

ps:该案例 2 紧跟案例 1 的脚步,所以完成此案例前,请先完成案例 1。 

(1)在 Hive 中创建外部表 

create external table relevance_customer( 
name string, 
order_numb string,
order_date string,
addr_city string,
addr_state string) 
stored by 'org.apache.hadoop.hive.hbase.HBaseStorageHandler' 
with serdeproperties 
("hbase.columns.mapping"=":key,
order:numb,
order:date,
addr:city,
addr:state") 
tblproperties("hbase.table.name" = "customer");

二、HBase 管理 

2.1 Region管理

前面介绍元数据部分的时候可以看出,一个 Region 就是一个表的一段Rowkey 的数据集合。

HBase 设计中,当某个 Region 太大的时候 HBase 会拆分它。那么为什么要拆分 Region?

因为当某个 Region 太大的时候读取效率太低了想想我们为什么从 ySQL、Oracle 转移到 NoSQL 来?最根本的原因就是这些关系型数据库把数据放到一个地方,查询的本质其实也就是遍历 key;而当数据增大到上亿的时候同一个磁盘已经无法应付这些数据的读取了,因为遍历一遍数据的时间实在太长了。

我们用 NoSQL 的理由就是其能把大数据分拆到不同的机器上,然后就像查询一个完整的数据一样查询他们。但是当你的 Region 太大的时候,此时这个 Region 一样会遇到跟传统关系型数据库一样的问题,所以 HBase 会拆分 Region。这也是 HBase 的一个优点,可以说 HBase 为“一个会自动分片的数据库”。 

Region 的拆分分为自动拆分和手动拆分。其中自动拆分可以采用不同的策略。

1.ConstantSizeRegionSplitPolicy

在 0.94 版本的时候 HBase 只有一种拆分策略。这个策略非常简单,从名字上就可以看出这个策略就是按照固定大小来拆分 Region。控制它的参数是: 

hbase.hregion.max.filesize      

Region 的最大大小。默认是 10GB

 当单个 Region 大小超过了 10GB,就会被 HBase 拆分成为 2 个 Region。采用这种策略后的集群中的 Region 大小很平均。由于这种策略太简单了,所以不再详细解释了

 2.IncreasingToUpperBoundRegionSplitPolicy(0.94 版本后默认) 

    0.94 版本之后,有了 IncreasingToUpperBoundRegionSplitPolicy 策略。

    这种策略从名字上就可以看出是限制不断增长的文件尺寸的策略。依赖以下公式来计算: 

Math.min(tableRegionsCounts^3 * initialSize,defaultRegionMaxFileSize) 

tableRegionCount:表在所有 RegionServer 上所拥有的 Region 数量总和。

initialSize:如果定义了 hbase.increasing.policy.initial.size,则使用这个数值。如果没有定义,就用 memstore 的刷写大小的 2 倍, hbase.hregion.memstore.flush.size * 2。 

defaultRegionMaxFileSize ConstantSizeRegionSplitPolicy 所 用 到 的 hbase.hregion.max.filesize,即 Region 最大大小。

假如 hbase.hregion.memstore.flush.size 定义为 128MB,那么文件尺寸的上限增长将是这样:  
(1)刚开始只有一个 region 的时候,上限是 256MB ,因为 1^3 *128*2=256MB。  
(2)当有 2 个 region 的时候,上限是 2GB,因为 2^3 * 128*2=2048MB。 
(3)当有 3 个文件的时候,上限是 6.75GB,因为 3^3 * 128 * 2=6912MB。 
(4)以此类推,直到计算出来的上限达到 hbase.hregion.max.filesize region 所定义的 10GB。 
 

走势图如下图:

菜鸟先飞之Hive与Hbase

当 Region 个数达到 4 个的时候由于计算出来的上限已经达到了 16GB,已经大于 10GB 了,所以后面当 Region 数量再增加的时候文件大小上限已经不会增加了。在最新的版本里 IncreasingToUpperBoundRegionSplitPolicy 是默认的配置。

2.1.2 Region 的预拆分  

预拆分(pre-splitting)就是在建表的时候就定义好了拆分点的算法,所以叫预拆分。

使用 org.apache.hadoop.hbase.util.RegionSplitter 类来创建表,并传入拆分点算法就可以在建表的同时定义拆分点算法。  

1.快速入门  

我们要新建一张表,并且规定了该表的 Region 数量永远只有 10 个。在 Linux下执行: 

hbase org.apache.hadoop.hbase.util.RegionSplitter my_split_table HexStringSplit -c 10 -f mycf my_split_table:

我们指定要新建的表名。 

HexStringSplit:指定的拆分点算法为 HexStringSplit。 

-c:要拆分的 Region 数量。 

-f:要建立的列族名称。 

ps:上面这条命令的意思就是新建一个叫 my_split_table 的表,并根据HexStringSplit 拆分点算法预拆分为 10 个 Region,同时要建立的列族叫 mycf。

建完后用 hbase shell 看一下结果。执行命令查出所有 10 个 Region 的信息:

scan 'hbase:meta',{STARTROW=>'my_split_table',LIMIT=>10} 

已经建立的 10 个 Region,由于输出信息太多,我只截取其中关于

每一个 Region 的起始 rowkey 和结束 rowkey 的信息,这 10 个 Region 的范围分别是:

STARTKEY=>'', ENDKEY=>'19999999'  

STARTKEY=>'19999999', ENDKEY=>'33333332'

STARTKEY=>'33333332', ENDKEY=>'4ccccccb'  STARTKEY=>'4ccccccb', ENDKEY=>'66666664'  

STARTKEY=>'66666664', ENDKEY=>'7ffffffd' 

STARTKEY=>'7ffffffd', ENDKEY=>'99999996'  

STARTKEY=>'99999996', ENDKEY=>'b333332f'  

STARTKEY=>'b333332f', ENDKEY=>'ccccccc8'  

STARTKEY=>'ccccccc8', ENDKEY=>'e6666661'  STARTKEY=>'e6666661', ENDKEY=>''  

这就是你预定了拆分点后的 Region。

2.拆分算法(扩展内容) 

(1)HexStringSplit 拆分算法 

在快速入门例子中使用的算法就是 HexStringSplit 算法。HexStringSplit 把数据从“00000000”到“FFFFFFFF”之间的数据长度按照 n 等分之后算出每一段的起始 rowkey 和结束 rowkey,以此作为拆分点。完毕,就是这么简单。  

(2)UniformSplit 拆分算法 

UniformSplit 有点像 HexStringSplit 的 byte 版,不管传参还是 n,唯一不一样的是起始和结束不是 String ,而是 byte[] 。起始 rowkey 是ArrayUtils.EMPTY_BYTE_ARRAY。结束rowkey是new byte[] {xFF, xFF, xFF, xFF, xFF, xFF, xFF, xFF}。最后调用 Bytes.split 方法把起始 rowkey 到结束 rowkey 之间的长度 n 等分,然后取每一段的起始和结束作为拆分点。默认的拆分点算法就这两个。还可以通过实现 SplitAlgorithm 接口实现自己的拆分算法。或者干脆手动定出拆分点。  

3.手动指定拆分点 

手动指定拆分点的方法就是在建表的时候跟上 SPLITS 参数,比如: 

create 'test_split2','mycf2',SPLITS=>['aaa','bbb','ccc','ddd','eee','fff'] 

上述语句在 HBase shell 中执行,执行完成后,可以通过 HBase Web UI 中查看相关信息

上面的手动分区是使用类似数组的形式指定的,除了这种方式外,还可以使用指定预分区文件的方式来操作。

(1)准备文件 

splits.txt aaa ... fff 

(2)创建预分区表 

create 'students','baseinfo',SPLITS_FILE => '/home/hadoop/data/splits.txt'

4.Region 的强制拆分 

除了预拆分和自动拆分以外,有时候可以对运行了一段时间的 Region 进行强制地手动拆分(forced splits)。方法是调用 hbase shell 的 split 方法,比如:对前面的 students 表中的 region 进行强制拆分。 

split '1bb4e6fef4002ad49946a6b13c0391a2','b' 

这个就是把 students 表中 1bb4e6fef4002ad49946a6b13c0391a2 这个 Region重新拆分成2个Region

5.推荐方案  

一开始可以先定义拆分点,但是当数据开始工作起来后会出现热点不均的情况,所以推荐的方法是:  

1)用预拆分导入初始数据。 

2)然后用自动拆分来让 HBase 来自动管理 Region。  建议:不要关闭自动拆分。 

6.总结  

Region 的拆分对性能的影响还是很大的,默认的策略已经适用于多数情况。

如果要调整,尽量不要调整到特别不适合你的策略,比如设置成 KeyPrefixRegionSplitPolicy,然后还用时间戳来做 rowkey。 

一种策略的选择要看多方面因素,有可能你的集群同时适合多种策略,这样就要看哪种策略效果最好了。

如果无法计算出来,就一个一个地尝试过去,用实践来检验真理