深入理解Hive分区与分桶

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解Hive分区与分桶相关的知识,希望对你有一定的参考价值。

参考技术A 目前,在利用hive建设数据仓库的过程中,总会遇见分区分桶的,跟传统的DBMS系统一样,通过表分区能够在特定的区域检索数据,减少扫描成本,在一定程度上提高查询效率。比如我们要收集某个大型网站的日志数据,一个网站每天的日志数据存在同一张表上,由于每天会生成大量的日志,导致数据表的内容巨大,在查询时进行全表扫描耗费的资源非常多。那其实这个情况下,我们可以按照日期对数据表进行分区,不同日期的数据存放在不同的分区,在查询时只要指定分区字段的值就可以直接从该分区查找。在物理上分区表会将数据按照分区键的列值存储在表目录的子目录中,目录名=“分区键=键值”。其中需要注意的是分区键的值不一定要基于表的某一列(字段),它可以指定任意值,只要查询的时候指定相应的分区键来查询即可。

分桶与分区有所不同,它指定分桶表的某一列,让该列数据按照哈希取模的方式随机、均匀地分发到各个桶文件中。因为分桶操作需要根据某一列具体数据来进行哈希取模操作,故指定的分桶列必须基于表中的某一列(字段)。因为分桶改变了数据的存储方式,它会把哈希取模相同或者在某一区间的数据行放在同一个桶文件中。如此一来便可提高查询效率,比如我们要对两张在同一列上进行了分桶操作的表进行JOIN操作的时候,只需要对保存相同列值的桶进行JOIN操作即可。同时分桶也可以提高采样率。

分区是为了对表进行合理的管理以及提高查询效率,Hive可以将表组织成“分区”。一个分区实际上就是表下的一个目录,一个表可以在多个维度上进行分区,分区之间的关系就是目录树的关系。Hive(Inceptor)分区分为静态分区跟动态分区,详细介绍如下:

静态分区在插入或者导入的时候需要指定具体的分区

[if !supportLists]1、  [endif]静态分区创建

需要在PARTITIONED BY后面跟上分区键,类型。例如:

CREATE TABLE p_table1(

id int

,name

string

)

PARTITIONED BY(date_day string)

stored as orc

;

这是一级分区,当然也可以创建多级分区。例如:

CREATE TABLE  p_table1(

id int

,name string

)

PARTITIONED BY(date_day string, company

string,emp_no string)

stored as orc

;                                                                          

下面的实例都是以一级分区为例。                     

[if !supportLists]2、  [endif]静态分区插入数据

insert overwrite table p_table1 partition(date_day='2019-07-14')

values(1,'lucy');

或者insert overwrite table p_table1 partition(date_day='2019-07-15')

select 2 as id,'lily' as name;

上面两个例子都是覆盖的形式,也就是插入这个分区之前,会将该分区数据删除,再插入新的数据,也可以写成追加的形式:

insert into p_table1

partition(date_day='2019-07-14') values(1,'lucy');

或者insert into e p_table1 partition(date_day='2019-07-15') select 2 as

id,'lily' as name;

[if !supportLists]3、  [endif]静态分区查看分区

查看所有分区show partitions p_table1

结果如下:

date_day=2019-07-14

date_day=2019-07-15

查看某个分区show partitions p_table1 partition(date_day='2019-07-14');

结果如下:

date_day=2019-07-14

[if !supportLists]4、  [endif]静态分区删除分区

删除某个分区alter table p_table1 drop partition(date_day='2019-07-14');

或者删除范围内的分区alter table p_table1 drop partition(date_day>='2019-07-14');

1、动态分区创建

创建方式与静态分区表完全一样,一张表可同时被静态和动态分区键分区,只是动态分区键需要放在静态分区建的后面(因为HDFS上的动态分区目录下不能包含静态分区的子目录)。

CREATE TABLE p_table2(

id int

,name string

)

PARTITIONED BY(date_day string,emp_no

string)

stored as orc

;

这是创建了二级分区表。

2、动态分区插入数据

插入数据时需要开启动态数据支持:

set hive.exec.dynamic.partition=true;

set hive.exec.dynamic.partition.mode=nostrict;

插入数据(覆盖)insert overwrite table p_table2 partition(date_day,emp_no)

select 2 as id,'lily' as name,'2019-07-14' as date_day, ‘a’ as emp_no;

分区并没有写死,而是根据查询到的值动态创建的两级分区。

3、动态分区查看分区、删除分区与静态分区操作完全一致不再重述。

分桶字段是表内字段,默认是对分桶的字段进行hash值,然后模总的桶数,得到的值则是分区桶数,主要有以下两点好处:

(1)获得更高的查询处理效率。桶为表加上了额外的结构,Hive 在处理有些查询时能利用这个结构。具体而言,连接两个在(包含连接列的)相同列上划分了桶的表,可以使用 Map 端连接(Map-side join)高效的实现。比如JOIN操作。对于JOIN操作两个表有一个相同的列,如果对这两个表都进行了桶操作。那么将保存相同列值的桶进行JOIN操作就可以,可以大大较少JOIN的数据量。

(2)使取样(sampling)更高效。在处理大规模数据集时,在开发和修改查询的阶段,如果能在数据集的一小部分数据上试运行查询,会带来很多方便。

创建分桶表

先看一下创建分桶表的创建,分桶表的建表有三种方式:直接建表,CREATE TABLE LIKE 和 CREATE TABLE AS SELECT ,单值分区表不能用 CREATETABLE

AS SELECT 建表。这里以直接建表为例:

create table b_table1(id int,name string)

clustered by (id) sorted by(id) into 4 buckets stored as textfile;

使用CLUSTERED BY 子句来指定划分桶所用的列和要划分的桶的个数,当表分区时,每个分区下都会有4个桶。对于map端连接的情况,两个表以相同方式划分桶。处理左边表内某个桶的 mapper知道右边表内相匹配的行在对应的桶内。因此,mapper只需要获取那个桶 (这只是右边表内存储数据的一小部分)即可进行连接。这一优化方法并不一定要求两个表必须桶的个数相同,两个表的桶个数是倍数关系也可以。用HiveQL对两个划分了桶的表进行连接。

桶中的数据可以根据一个或多个列另外进行排序。由于这样对每个桶的连接变成了高效的归并排序(merge-sort), 因此可以进一步提升map端连接的效率。

向分桶表写入数据

如何保证表中的数据都划分成桶了呢?把在Hive外生成的数据加载到划分成桶的表中,当然是可以的。其实让Hive来划分桶更容易。这一操作通常针对已有的表。

Hive并不检查数据文件中的桶是否和表定义中的桶一致(无论是对于桶的数量或用于划分桶的列)。如果两者不匹配,在査询时可能会碰到错误或未定义的结果。因此,建议让Hive来进行划分桶的操作。

要向分桶表中填充成员,需要将 hive.enforce.bucketing 属性设置为 true。这样Hive 就知道用表定义中声明的数量来创建桶。

下面有个未分桶的用户表b_user_test,数据如下:

1      a

2      b

3      c

4      d

5      e

6      f

7      g

插入语句

INSERT OVERWRITE TABLE b_table1 SELECT *

FROM b_user_test;

查看文件结构

dfs -ls/user/hive/warehouse/bucketed_users;

文件结构如下所示:

 /user/hive/warehouse/b_table1/000000_0

 /user/hive/warehouse/b_table1/000001_0

 /user/hive/warehouse/b_table1/000002_0

 /user/hive/warehouse/b_table1/000003_0

查看文件000000_0

dfs -cat /user/hive/warehouse/bucketed_users/000000_0;

值为4 d说明文件000000_0存的是对分桶数求余等于0的那部分数据。

对桶中的数据进行采样

对分桶进行查询 tablesample(bucket x out of y on id):

x:表示查询那个桶

y:表示建表指定的桶的总数,如果不是建表时指定的桶的总数,则会重新分桶。

x不能大于y。

取第一个桶的数据:

Sql:SELECT * FROM

b_table1 TABLESAMPLE(BUCKET 2 OUT OF 4 ON id);

结果:

5      e

[if !supportLists]1                       [endif]a

当桶数不等于建表指定的桶的总数时

Sql:SELECT * FROM

b_table1 TABLESAMPLE(BUCKET 2 OUT OF 3 ON id);

结果:

4      d

1      a

7      g

由结果可知,进行了重新分桶,分成了三个桶,取出第二个桶的数据,也就是hash值对3求余等于1的那部分数据。

分桶比分区粒度更细,在每个分区了可以将数据进行分桶操作。

Hive 教程-分区表与分桶表

在 hive 中分区表是很常用的,分桶表可能没那么常用,本文主讲分区表。

 

概念

分区表

在 hive 中,表是可以分区的,hive 表的每个区其实是对应 hdfs 上的一个文件夹;

可以通过多层文件夹的方式创建多层分区

通过文件夹把数据分开

 

分桶表

分桶表中的每个桶对应 hdfs 上的一个文件;

通过文件把数据分开

 

在查询时可以通过 where 指定分区(分桶),提高查询效率

 

分区表基本操作

1. 创建分区表

partitoned by 指定分区,后面加 分区字段 和 分区字段类型,可以加多个字段,前面是父路径,后面是子路径

create table student_p(id int,name string,sexex string,age int,dept string)
partitioned by(part string)
row format delimited fields terminated by ,
stored as textfile;    

分区表相当于给 表 加了一个字段,然后给这个字段赋予不同的 value,每个 value 对应一个分区,这个 value 对应 hdfs 上文件夹的名字

 

2. 写入数据

1, zhangsan, f, 30, a,
2, lisi, f, 39, b,
3, wangwu, m, 26, c,

写入两次,每次设置不同的分区

load data local inpath ‘/usr/lib/hive2.3.6/2.csv‘ into table student_p partition(part=321);
load data local inpath ‘/usr/lib/hive2.3.6/2.csv‘ into table student_p partition(part=456);

 3. 写入数据后看看长啥样

hive> select * from student_p;
OK
1    zhangsan    f    20    henan    321
2    lisi    f    30    shanghai    321
3    wangwu    m    40    beijing    321
1    zhangsan    f    20    henan    456
2    lisi    f    30    shanghai    456
3    wangwu    m    40    beijing    456
Time taken: 0.287 seconds, Fetched: 6 row(s)

4. hdfs 上看看长啥样 

技术图片

5. 查看某个分区

hive> select * from student_p where part=321;

6. 数据库里看看元数据信息

分区信息保存在 PARTITIONS 表中

技术图片

还有其他与  PARTITIONS 相关的表,自己可以看看

 

小结:每个分区对应一个文件夹,而且这个文件夹必须存储到元数据中;

也就是说,如果这个文件不在元数据中,那么即使他存在,也不是分区表中的一个分区,通过表查询不到

 

增加分区

加载数据时会自动增加分区,也可以不加载数据,单独创建分区

增加一个分区

hive> alter table student_p add partition(part=999);

增加多个分区

hive> alter table student_p add partition(part=555) partition(part=666);

技术图片

 

删除分区

删除一个分区

hive> alter table student_p drop partition(part=555);
Dropped the partition part=555
OK
Time taken: 0.675 seconds

删除多个分区

hive> alter table student_p drop partition(part=666), partition(part=999);
Dropped the partition part=666
Dropped the partition part=999
OK
Time taken: 0.464 seconds

 

查看分区数

hive> show partitions student_p;
OK
part=321
part=456
Time taken: 0.28 seconds, Fetched: 2 row(s)

 

查看分区表结构

hive> desc formatted student_p;
OK
# col_name                data_type               comment             
          
id                      int                                         
name                    string                                      
sexex                   string                                      
age                     int                                         
dept                    string                                      
          
# Partition Information          
# col_name                data_type               comment             
          
part                    string                                      
          
# Detailed Table Information          
Database:               hive1101                 
Owner:                  root                     
CreateTime:             Fri Nov 01 02:00:25 PDT 2019     
LastAccessTime:         UNKNOWN                  
Retention:              0                        
Location:               hdfs://hadoop10:9000/usr/hive_test/student_p     
Table Type:             MANAGED_TABLE            

 

表与数据关联

之前我们讲到如果一个文件夹在表目录下,但是不在元数据中,那么通过表是查不到这个数据的。

那如何把这种数据通过表读出来?必须把他们关联起来,有三种方式

 

上传数据后修复

1. 直接上传数据到 hdfs

hive> dfs -mkdir -p /usr/hive_test/student_p/part=888;
hive> dfs -put /usr/lib/hive2.3.6/2.csv /usr/hive_test/student_p/part=888;

在 hdfs 上直接建了一个目录,并且这个目录在 表目录下,然后给这个目录上传一个文件

2. 查询该分区数据,无果

3. 修复表

hive> msck repair table student_p;
OK
Partitions not in metastore:    student_p:part=888
Repair: Added partition to metastore student_p:part=888
Time taken: 0.502 seconds, Fetched: 2 row(s)

就是把分区添加到元数据

4. 查询可查到数据

 

上传数据后添加分区

首先执行上面的 1 2 步;

然后给表添加分区,把新建的文件夹添加给表做分区

hive> alter table student_p add partition(part=888);

 

创建文件夹后 load 数据到分区

我们知道 load 是会自动创建分区的,所以这样肯定可以

 

创建二级分区

二级分区,也就是多层分区,也就是多层路径

 

创建多级分区表

create table student1102(id int,name string,sexex string,age int,dept string)
partitioned by(month string, day int)
row format delimited fields terminated by ,
stored as textfile; 

month 一级,day 是month 下一级

 

load 数据

load data local inpath /usr/lib/hive2.3.6/2.csv into table student1102 partition(month=11, day=2);

 

在 hdfs 一看就知道怎么回事了

技术图片

 

查询数据

hive> select * from student1102 where month=11 and day=2;

加个 and 就可以了

 

加载数据

load data inpath /user/tuoming/test/test  into table part_test_3 partition(month_id=201805,day_id=20180509); 追加
load data inpath /user/tuoming/test/test overwrite into table part_test_3 partition(month_id=201805,day_id=20180509);  覆盖

insert overwrite table part_test_3 partition(month_id=201805,day_id=20180509) select * from part_test_temp; 覆盖
insert into part_test_3 partition(month_id=201805,day_id=20180509) select * from part_test_temp; 追加

 

动态分区

参考下面的参考资料

 

 

 

参考资料:

https://blog.csdn.net/afafawfaf/article/details/80249974

以上是关于深入理解Hive分区与分桶的主要内容,如果未能解决你的问题,请参考以下文章

Hive表的分区与分桶

Hive的分区表与分桶表&内部表外部表

Hive分区表与分桶表的使用具体说明

Hive SQL之分区表与分桶表

打怪升级之小白的大数据之旅(六十六)<Hive旅程第七站:Hive的分区表与分桶表>

打怪升级之小白的大数据之旅(六十六)<Hive旅程第七站:Hive的分区表与分桶表>