欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

一文彻底学会hive分桶表(实战详解)

程序员文章站 2022-03-08 14:24:40
...

一、分桶表概述

1.1、什么是分桶表?

分桶是将数据集分解成更容易管理的若*分的一个技术,是比表或分区更为细粒度的数据范围划分。针对某一列进行桶的组织,对列值哈希,然后除以桶的个数求余,决定将该条记录存放到哪个桶中。

常用于:

  • 获得更高的查询处理效率
  • 抽样调查
1.2、分桶表和分区表有啥区别?

分区表提供了一个隔离数据和优化查询的便利方式。但是在实际场景下,并非所有的数据集都可形成合理的分区。对于一张表或者分区,Hive还可以进一步组织成桶,也就是更为细粒度的数据范围划分。

小结一波:

  1. 分桶对数据的处理比分区更加细粒度化;
  2. 分桶和分区两者不干扰,可以把分区表进一步分桶;
  3. 分区针对的是数据的存储路径;分桶针对的是数据文件。

二、分桶表实战

首先说明一下,我的hive环境是基于docker-compose构建,使用postgresql管理metastore,各重要组件版本如下:

hadoop:2.7.4

hive:2.3.2

java:1.8

其实环境不重要,不用花太多时间,主要还是技术部分,接下来就开始实战吧。

2.1、创建一个分桶表

我们创建一个简单的分桶表,只有两个字段(id,name),并且按照id进行分6个桶,sql如下:

create table test_buck(id int, name string)
clustered by(id) 
into 6 buckets
row format delimited fields terminated by '\t';

执行并查看表结构:
一文彻底学会hive分桶表(实战详解)

使用desc formatted test_buck;查看test_buck表结构,数据较多,这里截取重要数据如下:
一文彻底学会hive分桶表(实战详解)

2.2、准备数据

准备一些编号和字符串并按照 "\t"分隔开,把这些数据放入/tools/目录下的test_buck.txt文件内,数据格式如下图:
一文彻底学会hive分桶表(实战详解)

2.3、向分桶表导入数据
2.3.1、错误导入示范(引出分桶的本质)

数据准备好之后,我们就可以向分桶表里导入数据里。这里我首先使用如下常规命令导入:

load data local inpath '/tools/test_buck.txt' into table test_buck;

然后很惊奇的是它报错了,报错信息如下:
一文彻底学会hive分桶表(实战详解)

简单翻译一下,报错信息是这样说的:请把数据导入到一个中间表,并使用insert … select语句导入到分桶表里。如果你非要直接导入分桶表里,需要把hive.strict.checks.bucketing设置为false(关闭严格模式),然后你写sql就没有检查了。。。。。

哈哈,到这里,我们知道了分桶表应该怎么导入数据了,但是为啥直接导入就不行呢?下面让我们硬着头皮改为false,然后导入数据试一下。

set hive.strict.checks.bucketing = false;

一文彻底学会hive分桶表(实战详解)
然后我们打开50070查看hdfs上数据如下:

一文彻底学会hive分桶表(实战详解)
说好的分6个桶呢?为什么只有一个呢?

这是因为:

桶的概念就是MapReduce的分区的概念,两者是完全一样的。也就是说物理上每个桶就是目录里的一个文件,一个作业产生的桶(输出文件)数量和reduce任务个数相同

所以这里我们导入一个文件,由于分区数等于文件数,只有一个分区,所以上面的操作并没有分桶。

2.3.2、正确导入示范(引出分桶规则)

绕了一圈,感觉还是把hive.strict.checks.bucketing设置为true比较好,节省犯错成本(不过有时候多犯错也是好事,比如上面咱们至少知道了hive分桶本质上就是mapreduce的分区)。

把hive严格模式打开,并把桶表清空:

set hive.strict.checks.bucketing = true;
truncate table test_buck;

下面我们使用之前严格模式下建议我们使用的分桶表导入数据方式,使用中间表来进行数据导入:

  1. 设置强制分桶为true,并设置reduce数量为分桶的数量个数。

    set hive.enforce.bucketing = true;
    

    一文彻底学会hive分桶表(实战详解)

  2. 创建一个临时表,并导入数据。

    create table temp_buck(id int, name string)
    row format delimited fields terminated by '\t';
    load data local inpath '/tools/test_buck.txt' into table temp_buck;
    

    一文彻底学会hive分桶表(实战详解)

  3. 使用insert select来间接把数据导入到分桶表。

    insert into table test_buck select id, name from temp_buck;
    
  4. 在50070上查看分桶表的数据,已经分为了6个,桶表数据插入成功!
    一文彻底学会hive分桶表(实战详解)

  5. 查询分桶表里的数据。

    select * from test_buck;
    

    结果如下图,仔细观察:
    一文彻底学会hive分桶表(实战详解)

通过观察规则和桶数的对应关系,我们可以得出如下分桶规则

  • Hive的分桶采用对分桶字段的值进行哈希,然后除以桶的个数求余的方式决定该条记录存放在哪个桶当中
2.4、分桶抽样

对表分桶一般有两个目的,提高数据查询效率、抽样调查。通过前面的实战,我们已经可以对分桶表进行正常的创建并导入数据了。一般在实际生产中,对于非常大的数据集,有时用户需要使用的是一个具有代表性的查询结果而不是全部结果。这个时候Hive就可以通过对表进行抽样来满足这个需求。

抽样语句语法解析:

tablesample(bucket x out of y)
y必须是table总bucket数的倍数或者因子。hive根据y的大小,决定抽样的比例。
例如,table总共分了6份,当y=2时,抽取(6/2=)3个bucket的数据,当y=8时,抽取(6/8=)3/4个bucket的数据。
x表示从哪个bucket开始抽取,如果需要取多个分区,以后的分区号为当前分区号加上y。
例如,table总bucket数为6,tablesample(bucket 1 out of 2),表示总共抽取(6/2=)3个bucket的数据,抽取第1(x)个和第3(x+y)个和第5(x+y)个bucket的数据。
注意:x的值必须小于等于y的值

举例:

select * from test_buck tablesample(bucket 1 out of 3 on id);

结果如下图,可以发现,上面这条sql查出了第1个和第4个桶的数据:
一文彻底学会hive分桶表(实战详解)

三、总结

3.1、分桶表的优点
  1. 查询效率更快,因为分桶为表加上了额外结构,链接相同列划分了桶的表,可以使用map-side join更加高效;
  2. 由于分桶规则保证了数据在不同桶的随机性,因此平时进行抽样调查时取样更加方便。
3.2、常用操作
  1. 建表语句及含义

    create table buck_table_name (id int,name string)
    clustered by (id) sorted by (id asc) into 4 buckets
    row format delimited fields terminated by '\t';
    注意:CLUSTERED BY来指定划分桶所用列和划分桶的个数。
    分桶规则:HIVE对key的hash值除bucket个数取余数,保证数据均匀随机分布在所有bucket里。
    SORTED BY对桶中的一个或多个列另外排序
    
  2. 导入数据

    先把数据导入中间表,然后使用insert select语句进行间接导入到分桶表,比如咱们实战的例子:

    insert into table test_buck select id, name from temp_buck;
    
3.3、分桶表的实质及与分区表的区别

hive的桶其实就是MapReduce的分区的概念,两者完全相同。在物理上每个桶就是目录里的一个文件,一个作业产生的桶(输出文件)数量和reduce任务个数相同。

分区代表了数据的仓库,也就是文件夹目录。每个文件夹下面可以放不同的数据文件。通过文件夹可以查询里面存放的文件。但文件夹本身和数据的内容毫无关系。

桶则是按照数据内容的某个值进行分桶,把一个大文件散列称为一个个小文件。这些小文件可以单独排序。如果另外一个表也按照同样的规则分成了一个个小文件。两个表join的时候,就不必要扫描整个表,只需要匹配相同分桶的数据即可,效率当然大大提升。

同样的道理,在对数据抽样的时候,也不需要扫描整个文件。只需要对每个分区按照相同规则抽取一部分数据即可。