Hive的分区


Hive提供了分区的概念,对应到后台就是不同的目录,主要是为了在查询的时候可以提高性能。而分区分为两种:

  • 静态分区(Static Partition,以下简称SP):用户指定分区列的值,数据中不包含分区列的值,所以分区与数据没有关系。举个例子比如分区名字为ds=20180922,并不代表分区内的数据都是这天的,这个需要由用户自己保证。静态分区在编译阶段确定。
  • 动态分区(Dynamic Partition,以下简称DP):分区列的值从数据中获取,所以数据列中包含分区列的值。比如数据中有一列数据为日期,如果指定该列为分区列的话,那将根据该列的值创建分区。动态分区在运行阶段才可以确定。

今天就来介绍这两种分区。

假设现在有这样一个场景:我们有一些用户数据(UserRecords.txt)需要存储在Hive里面,数据里面包含了国家和地区信息,根据业务需求我们要将不同国家不同地区的用户存储在不同的分区里面。也就是说我们需要使用国家和的确信息作为分区列。下面我们看如果通过两种分区去实现。

静态分区

静态分区非常简单,之前的文章中已经提到过,就是我们导入数据的时候指定分区列的值,然后到后台就会用这个值建对应的目录,多个分区列就是嵌套的多级目录(所以Hive里面的分区列的顺序是有意义的)。

首先我们先将UserRecords.txt数据导入到Hive里面的一张临时表temp_user里面:

# 原始表
CREATE TABLE temp_user (
    firstname  VARCHAR(64),
    lastname    VARCHAR(64),
    address     STRING,
    country     VARCHAR(64),
    city        VARCHAR(64),
    state       VARCHAR(64),
    post        STRING,
    phone1      VARCHAR(64),
    phone2      STRING,
    email       STRING,
    web         STRING
) 
    ROW FORMAT DELIMITED
        FIELDS TERMINATED BY ','
        LINES TERMINATED BY '\n';

# 导入原始数据
LOAD DATA LOCAL INPATH '/tmp/UserRecords.txt' INTO TABLE temp_user;

然后创建静态分区表,使用国家和地区作为分区列:

# 创建静态分区表
CREATE TABLE sp_user (
    firstname  VARCHAR(64),
    lastname    VARCHAR(64),
    address     STRING,
    city        VARCHAR(64),
    post        STRING,
    phone1      VARCHAR(64),
    phone2      STRING,
    email       STRING,
    web         STRING
)    
    PARTITIONED BY(country VARCHAR(64), state VARCHAR(64))
    ROW FORMAT DELIMITED
        FIELDS TERMINATED BY ','
        LINES TERMINATED BY '\n';
        

现在的问题来了,如何将temp_user中的数据按照分区导入到sp_user里面?静态分区是用户在导入数据的时候指定的,显然我们需要知道有哪些分区,知道以后,就可以使用下面的语句分多次导入了:

# 这里以导入数据US、CA的数据为例
INSERT INTO TABLE sp_user 
    PARTITION(country='US', state='CA')
    SELECT firstname, lastname, address, city, post, phone1, phone2, email, web
    FROM temp_user 
    WHERE country == 'US' AND state == 'CA';


# 对应HDFS上面的存储
➜  hadoop-2.7.7 hadoop fs -ls -R /user/hive/warehouse/sp_user
drwxrwxr-x   - anonymous supergroup          0 2018-09-21 16:31 /user/hive/warehouse/sp_user/country=US
drwxrwxr-x   - anonymous supergroup          0 2018-09-21 16:38 /user/hive/warehouse/sp_user/country=US/state=CA
-rwxrwxr-x   1 anonymous supergroup       9430 2018-09-21 16:38 /user/hive/warehouse/sp_user/country=US/state=CA/000000_0

显然,如果国家和地区的组合很多的话,这样做会累死人。这个时候其实便可以考虑使用动态分区了。

动态分区

动态分区的"动"主要体现在分区列的值不是用户指定的,而是在运行阶段根据列值确定的。对于刚才的需求我们先创建分区表:

# 动态分区表
CREATE TABLE dp_user (
    firstname  VARCHAR(64),
    lastname    VARCHAR(64),
    address     STRING,
    city        VARCHAR(64),
    post        STRING,
    phone1      VARCHAR(64),
    phone2      STRING,
    email       STRING,
    web         STRING
)
    PARTITIONED BY(country VARCHAR(64), state VARCHAR(64))
    ROW FORMAT DELIMITED
        FIELDS TERMINATED BY ','
        LINES TERMINATED BY '\n';
        

建表语句与静态分区表一模一样,没有任何区别,动态与静态的区别主要在数据导入上面。不过在导入之前,我们需要先简单介绍几个重要的跟动态分区相关的参数:

  • hive.exec.dynamic.partition:是否允许动态分区,0.9.0之前的版本里面默认为false,后续版本默认为true。要使用动态分区,该参数必须为true,可通过set hive.exec.dynamic.partition;命令查看。
  • hive.exec.dynamic.partition.mode:动态分区的模式,可选值为strict(默认值)和nonstrictstrict模式下要求分区列里面至少要有一个静态分区,而nonstrict模式没有这个要求。
  • hive.exec.max.dynamic.partitions:允许创建的最大分区数目,默认值为1000。
  • hive.exec.max.dynamic.partitions.pernode:每一个mapper/reducer节点允许创建的动态分区数据,默认值为100。
  • hive.exec.max.created.files:允许一个MR任务里面所有mapper/reducer创建的HDFS文件的最大数目,默认值为100000。
  • hive.error.on.empty.partition:是否允许动态分区产生没有数据的分区,默认为false。

在我们这个需求里面需要做如下修改:

# 我们的分区列里面全部是动态分区,所以需要修改为nonstrict
set hive.exec.dynamic.partition.mode=nonstrict;

# 创建的分区比较多,100不够用,调整为1000
set hive.exec.max.dynamic.partitions.pernode=1000;

然后就可以导入了:

INSERT INTO TABLE dp_user 
    PARTITION(country, state)
    SELECT firstname, lastname, address, city, post, phone1, phone2, email, web,
    country, state
    FROM temp_user;

可以看到和静态分区导入的区别主要有两点:

  • 静态分区我们指定了分区列的值,而动态分区没有指定分区列的值;
  • 静态分区导入的时候分区列没有在普通的字段里面,而动态分区导入的时候分区列也在普通字段里面。

然后我们看一下效果:

# Hive里面

0: jdbc:hive2://localhost:10000> SHOW PARTITIONS dp_user;
+--------------------------------------------+
|                 partition                  |
+--------------------------------------------+
| country=AU/state=AC                        |
| country=AU/state=NS                        |
| country=AU/state=NT                        |
| country=AU/state=QL                        |
| country=AU/state=SA                        |
| country=AU/state=TA                        |
| country=AU/state=VI                        |
| country=AU/state=WA                        |
| country=CA/state=AB                        |
| country=CA/state=BC                        |
| country=CA/state=MB                        |
| country=CA/state=NB                        |
| country=CA/state=NL                        |
| country=CA/state=NS                        |
| country=CA/state=ON                        |
| country=CA/state=PE                        |
| country=CA/state=QC                        |
| country=CA/state=SK                        |
| country=UK/state= Uphall and Winchburg%22  |
| country=UK/state= Uplawmoor and Newton%22  |
| country=UK/state=%22Yorkshire              |
| country=UK/state=Aberdeenshire             |
…… 限于篇幅,省略部分输出 ……

# HDFS上面

➜  hadoop-2.7.7 hadoop fs -ls -R /user/hive/warehouse/dp_user
18/09/21 17:07:38 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
drwxrwxr-x   - allan supergroup          0 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU
drwxrwxr-x   - allan supergroup          0 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=AC
-rwxrwxr-x   1 allan supergroup        788 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=AC/000000_0
drwxrwxr-x   - allan supergroup          0 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=NS
-rwxrwxr-x   1 allan supergroup      16765 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=NS/000000_0
drwxrwxr-x   - allan supergroup          0 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=NT
-rwxrwxr-x   1 allan supergroup        929 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=NT/000000_0
drwxrwxr-x   - allan supergroup          0 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=QL
-rwxrwxr-x   1 allan supergroup      14926 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=QL/000000_0
drwxrwxr-x   - allan supergroup          0 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=SA
-rwxrwxr-x   1 allan supergroup       5997 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=SA/000000_0
drwxrwxr-x   - allan supergroup          0 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=TA
-rwxrwxr-x   1 allan supergroup       4331 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=TA/000000_0
drwxrwxr-x   - allan supergroup          0 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=VI
-rwxrwxr-x   1 allan supergroup      12132 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=VI/000000_0
drwxrwxr-x   - allan supergroup          0 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=WA
-rwxrwxr-x   1 allan supergroup      11068 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=WA/000000_0
…… 限于篇幅,省略部分输出 ……

可以看到,动态分区非常方便的帮我们实现了上面的需求,创建哪些分区是由数据决定,Hive自动帮我们创建的。

动态分区虽然好,但用的不对也会产生非常多的问题:最常见的比如创建太多的目录和文件、耗用内存等,这也是早期版本默认关闭该功能,以及现在的版本虽然默认允许,但也限制为strict的部分原因。这里有一篇文章给出了一些最佳实践,有兴趣的可以看一下:HIVE Dynamic Partitioning tips

关于分区最后再提两个注意点:

  • 分区表不能使用LOAD DATA的方式导入数据,一般都是使用INSERT语句导入。
  • 动态分区和静态分区一起用的时候,静态分区列必须写在动态分区列之前

分区修改DDL

注意:在Hive里面,关键字以及各种名字(数据库、分区、字段等)都是不区分大小写的。

除了查看分区的命令意外,其它和分区有关的命令都是操作表语句的一个子选项。前面部分我们已经用到了分区的创建、查看命令,最后再补充一些分区修改的DDL,包括:add、rename、exchanged(moved)、drop、(un)archive。这些都是ALTER TABLE命令的子选项。

  • 增加分区(add)
# 语法
ALTER TABLE table_name ADD [IF NOT EXISTS] PARTITION partition_spec [LOCATION 'location'][, PARTITION partition_spec [LOCATION 'location'], ...];
 
partition_spec:
  : (partition_column = partition_col_value, partition_column = partition_col_value, ...)
  
# 例子
ALTER TABLE sp_user ADD PARTITION (country='CN', state='Shannxi') location '/user/hive/warehouse/sp_user/country=CN/state=Shannxi'
                        PARTITION (country='CN', state='Shanxi') location '/user/hive/warehouse/sp_user/country=CN/state=Shanxi';
    
# HDFS上                        
➜  hadoop-2.7.7 hadoop fs -ls -R /user/hive/warehouse/sp_user
drwxrwxr-x   - allan supergroup          0 2018-09-22 11:12 /user/hive/warehouse/sp_user/country=CN
drwxrwxr-x   - allan supergroup          0 2018-09-22 11:12 /user/hive/warehouse/sp_user/country=CN/state=Shannxi
drwxrwxr-x   - allan supergroup          0 2018-09-22 11:12 /user/hive/warehouse/sp_user/country=CN/state=Shanxi
drwxrwxr-x   - allan supergroup          0 2018-09-21 16:31 /user/hive/warehouse/sp_user/country=US
drwxrwxr-x   - allan supergroup          0 2018-09-21 16:38 /user/hive/warehouse/sp_user/country=US/state=CA
-rwxrwxr-x   1 allan supergroup       9430 2018-09-21 16:38 /user/hive/warehouse/sp_user/country=US/state=CA/000000_0                        
  • 重命名(rename)
# 语法
ALTER TABLE table_name PARTITION partition_spec RENAME TO PARTITION partition_spec;

# 例子
ALTER TABLE sp_user PARTITION(country='CN', state='Shanxi') RENAME TO PARTITION(country='CN', state='Beijing');

# HDFS上
➜  hadoop-2.7.7 hadoop fs -ls -R /user/hive/warehouse/sp_user
drwxrwxr-x   - allan supergroup          0 2018-09-22 11:14 /user/hive/warehouse/sp_user/country=CN
drwxrwxr-x   - allan supergroup          0 2018-09-22 11:12 /user/hive/warehouse/sp_user/country=CN/state=Beijing
drwxrwxr-x   - allan supergroup          0 2018-09-22 11:12 /user/hive/warehouse/sp_user/country=CN/state=Shannxi
drwxrwxr-x   - allan supergroup          0 2018-09-21 16:31 /user/hive/warehouse/sp_user/country=US
drwxrwxr-x   - allan supergroup          0 2018-09-21 16:38 /user/hive/warehouse/sp_user/country=US/state=CA
-rwxrwxr-x   1 allan supergroup       9430 2018-09-21 16:38 /user/hive/warehouse/sp_user/country=US/state=CA/000000_0
  • 移动分区(exchange/move partition):在两个结构一样的表之间移动分区
# 语法
-- Move partition from table_name_1 to table_name_2
ALTER TABLE table_name_2 EXCHANGE PARTITION (partition_spec) WITH TABLE table_name_1;
-- multiple partitions
ALTER TABLE table_name_2 EXCHANGE PARTITION (partition_spec, partition_spec2, ...) WITH TABLE table_name_1;

# 例子:将dp_user的country=AU/state=AC分区移动到sp_user
ALTER TABLE sp_user EXCHANGE PARTITION(country='AU', state='AC') WITH TABLE dp_user;

# HDFS上
➜  hadoop-2.7.7 hadoop fs -ls -R /user/hive/warehouse/sp_user
18/09/22 11:20:11 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
drwxrwxr-x   - allan supergroup          0 2018-09-22 11:19 /user/hive/warehouse/sp_user/country=AU
drwxrwxr-x   - allan supergroup          0 2018-09-21 15:10 /user/hive/warehouse/sp_user/country=AU/state=AC
-rwxrwxr-x   1 allan supergroup        788 2018-09-21 15:10 /user/hive/warehouse/sp_user/country=AU/state=AC/000000_0
drwxrwxr-x   - allan supergroup          0 2018-09-22 11:14 /user/hive/warehouse/sp_user/country=CN
drwxrwxr-x   - allan supergroup          0 2018-09-22 11:12 /user/hive/warehouse/sp_user/country=CN/state=Beijing
drwxrwxr-x   - allan supergroup          0 2018-09-22 11:12 /user/hive/warehouse/sp_user/country=CN/state=Shannxi
drwxrwxr-x   - allan supergroup          0 2018-09-21 16:31 /user/hive/warehouse/sp_user/country=US
drwxrwxr-x   - allan supergroup          0 2018-09-21 16:38 /user/hive/warehouse/sp_user/country=US/state=CA
-rwxrwxr-x   1 allan supergroup       9430 2018-09-21 16:38 /user/hive/warehouse/sp_user/country=US/state=CA/000000_0

# 由前文的图知道原来第一个分区是country=AU/state=AC,现在被移走了
➜  hadoop-2.7.7 hadoop fs -ls -R /user/hive/warehouse/dp_user | head
drwxrwxr-x   - allan supergroup          0 2018-09-22 11:19 /user/hive/warehouse/dp_user/country=AU
drwxrwxr-x   - allan supergroup          0 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=NS
-rwxrwxr-x   1 allan supergroup      16765 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=NS/000000_0
drwxrwxr-x   - allan supergroup          0 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=NT
-rwxrwxr-x   1 allan supergroup        929 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=NT/000000_0
drwxrwxr-x   - allan supergroup          0 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=QL
-rwxrwxr-x   1 allan supergroup      14926 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=QL/000000_0
drwxrwxr-x   - allan supergroup          0 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=SA
-rwxrwxr-x   1 allan supergroup       5997 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=SA/000000_0
drwxrwxr-x   - allan supergroup          0 2018-09-21 15:10 /user/hive/warehouse/dp_user/country=AU/state=TA
  • 删除分区(drop):同时删除HDFS上面的数据和MetaStore里面的元数据
# 语法
ALTER TABLE table_name DROP [IF EXISTS] PARTITION partition_spec[, PARTITION partition_spec, ...]
  [IGNORE PROTECTION] [PURGE];            -- (Note: PURGE available in Hive 1.2.0 and later, IGNORE PROTECTION not available 2.0.0 and later)

# 例子
ALTER TABLE sp_user DROP IF EXISTS PARTITION(country='AU', state='AC');
  • 归档(archive):将分区的文件归档到Hadoop Archive(HAR),这样可以减少文件数量,但不做任何压缩.默认该功能是disabled的。
# 语法
ALTER TABLE table_name ARCHIVE PARTITION partition_spec;
ALTER TABLE table_name UNARCHIVE PARTITION partition_spec;
  • 恢复分区(recover):有时我们会绕过Hive直接在HDFS上面操作Hive文件,这样会导致Hive的元数据与HDFS上面的不一致,所以Hive提供了MSCK命令来同步HDFS上面的数据与MetaStore里面的元数据信息。
# 语法
MSCK [REPAIR] TABLE table_name [ADD/DROP/SYNC PARTITIONS];

# 例子
## 现在HDFS上创建分区目录
➜  hadoop-2.7.7 hadoop fs -mkdir /user/hive/warehouse/sp_user/country=CN/state=Shanghai
➜  hadoop-2.7.7 hadoop fs -ls -R /user/hive/warehouse/sp_user
drwxrwxr-x   - allan supergroup          0 2018-09-22 11:44 /user/hive/warehouse/sp_user/country=CN
drwxr-xr-x   - allan supergroup          0 2018-09-22 11:44 /user/hive/warehouse/sp_user/country=CN/state=Shanghai
drwxrwxr-x   - allan supergroup          0 2018-09-22 11:12 /user/hive/warehouse/sp_user/country=CN/state=Shannxi
drwxrwxr-x   - allan supergroup          0 2018-09-21 16:31 /user/hive/warehouse/sp_user/country=US
drwxrwxr-x   - allan supergroup          0 2018-09-21 16:38 /user/hive/warehouse/sp_user/country=US/state=CA
-rwxrwxr-x   1 allan supergroup       9430 2018-09-21 16:38 /user/hive/warehouse/sp_user/country=US/state=CA/000000_0

# 此时Hive里面的分区信息不包含创建的目录
0: jdbc:hive2://localhost:10000> SHOW PARTITIONS sp_user;
+---------------------------+
|         partition         |
+---------------------------+
| country=CN/state=Shannxi  |
| country=US/state=CA       |
+---------------------------+

# 使用MSCK同步元数据
0: jdbc:hive2://localhost:10000> MSCK REPAIR TABLE sp_user;
No rows affected (0.46 seconds)
0: jdbc:hive2://localhost:10000> SHOW PARTITIONS sp_user;
+----------------------------+
|         partition          |
+----------------------------+
| country=CN/state=Shanghai  |
| country=CN/state=Shannxi   |
| country=US/state=CA        |
+----------------------------+
4 rows selected (0.198 seconds)

需要注意:

  • MSCK REPAIR命令要慎重执行,因为非常耗费系统资源;
  • 我试了一下ADD/DROP/SYNC命令会报语法错误,暂时也没找到问题原因,不知道是不是版本不支持(我的Hive是2.3.3),但看官方说明(HIVE-17824)这个版本应该是支持的。

Hive的分区在实际中用的比较多,最常用的分区列就是时间、地理位置等,本文就介绍到这里。

References

本文链接:https://time-track.cn/hive-partition.html,转载请注明出处。
赞赏


微信赞赏

支付宝赞赏

添加新评论

选择表情

友情提醒:因为垃圾评论太多,首次评论需要审核。

|