Dynamic Partitions

Documentation

这是 Hive 中动态分区的design文档。使用信息也可用:

References:

Terminology

  • 静态分区(SP)列:在 DML/DDL 中,涉及多个分区列,这些列的值在 COMPILE TIME 时已知(由用户提供)。

  • 动态分区(DP)列:其值仅在执行时间才知道的列。

Syntax

在 partition 子句中,以与 SP 列相同的方式指定 DP 列。唯一的区别是 DP 列没有值,而 SP 列却没有。在 partition 子句中,我们需要指定所有分区列,即使它们都是 DP 列也是如此。

在 INSERT ... SELECT ...查询中,必须在 SELECT 语句的列中“最后指定” 动态分区列,并 它们在 PARTITION()子句中出现的 Sequences**。

  • 所有 DP 列-仅在非严格模式下允许。在严格模式下,我们应该抛出一个错误。例如。,
INSERT OVERWRITE TABLE T PARTITION (ds, hr) 
   SELECT key, value, ds, hr FROM srcpart WHERE ds is not null and hr>10;
  • SP 和 DP 混合柱。例如。,
INSERT OVERWRITE TABLE T PARTITION (ds='2010-03-03', hr) 
   SELECT key, value, /*ds,*/ hr FROM srcpart WHERE ds is not null and hr>10;
  • SP 是 DP 的子分区:应该抛出错误,因为分区列 Sequences 决定了目录层次结构。我们无法在 DML 中更改层次结构。例如。,
-- throw an exception
   INSERT OVERWRITE TABLE T PARTITION (ds, hr = 11) 
   SELECT key, value, ds/*, hr*/ FROM srcpart WHERE ds is not null and hr=11;
  • 多表插入。例如。,
FROM S
   INSERT OVERWRITE TABLE T PARTITION (ds='2010-03-03', hr) 
   SELECT key, value, ds, hr FROM srcpart WHERE ds is not null and hr>10
   INSERT OVERWRITE TABLE R PARTITION (ds='2010-03-03, hr=12)
   SELECT key, value, ds, hr from srcpart where ds is not null and hr = 12;
  • CTAS –语法与未分区表上的 CTAS 略有不同,因为目标表的架构并非完全来自于选择子句。我们需要指定创建子句中包含分区列的架构。例如。,
CREATE TABLE T (key int, value string) PARTITIONED BY (ds string, hr int) AS 
   SELECT key, value, ds, hr+1 hr1 FROM srcpart WHERE ds is not null and hr>10;

上面的示例显示了 CTAS 中所有 DP 列的情况。如果要在某些分区列中放置常量,则可以在选择子句中指定它。例如,

CREATE TABLE T (key int, value string) PARTITIONED BY (ds string, hr int) AS 
   SELECT key, value, "2010-03-03", hr+1 hr1 FROM srcpart WHERE ds is not null and hr>10;

Design

  • 在 SemanticAnalyser.genFileSinkPlan()中,解析 Importing 并生成 SP 和 DP 列的列表。我们还将在 FileSinkDesc 中生成从 ImportingExpressionNode 到输出 DP 列的 Map。

  • 我们还需要在 FileSinkDesc 中保留一个 HashFunction 类,以根据 Importing 表达式值评估分区目录名称。

  • 在 FileSinkOperator 中,在 initOp()中设置 Importing-> DPMap 和哈希。并根据 Map 确定 processOp()中的输出路径。

  • ObjectInspector setup?

  • MoveTask:由于 DP 列代表一个子目录树,因此可以在末尾使用一个 MoveTask 将结果从临时目录移动到最终目录。

  • post exec 钩子进行复制:在创建新分区之前,请删除 DP 中所有现有数据。我们应该确保复制钩子能够识别所有修改后的分区。

  • metastore 支持:由于我们正在 DML 中创建多个分区,因此 metastore 应该能够创建所有这些分区。需要调查。

Design issues

1)动态分区列的数据类型:
动态分区列可能是表达式的结果。例如:

-- part_col is partitioning column
 create table T as select a, concat("part_", part_col) from S where part_col is not null;

尽管当前对分区列的数据类型没有限制,但允许非原始列成为分区列可能没有任何意义。动态分区列的类型应从表达式派生。数据类型必须能够转换为字符串才能保存为 HDFS 中的目录名称。

2)将列值分区为目录名称转换:
将列值转换为字符串后,我们仍然需要将字符串值转换为有效的目录名称。原因如下:

  • 字符串长度在理论上是无限的,但是 HDFS /本地 FS 目录名称的长度是有限的。

  • 字符串值可以包含在 FS 路径名中保留的特殊字符(例如'/'或'..')。

  • 分区列 ObjectInspector 应该怎么做?

我们需要定义一个 UDF(例如 hive_qname_partition(T.part_col)),以获取基本类型的值并将其转换为合格的分区名称。

3)由于 2),此动态分区方案符合基于哈希的分区方案,但我们将哈希函数定义为与
Importing 值。我们应该允许用户为分区哈希函数插入自己的 UDF。如果有足够的兴趣,将提交跟进 JIRA。

4)如果有多个分区列,则它们的 Sequences 很重要,因为这会转换为 HDFS 中的目录结构:按(ds 字符串,dept int)进行分区意味着 ds = 2009-02-26/dept = 2 的目录结构。在涉及分区表的 DML 或 DDL 中,因此,如果指定了分区列的子集(静态),则动态分区列较低时,我们将引发错误。例:

create table nzhang_part(a string) partitioned by (ds string, dept int);
 insert overwrite nzhang_part (dept=1) select a, ds, dept from T where dept=1 and ds is not null;