Hive 修改 partition 逻辑分析

281 阅读2分钟

问题

由于传递 hive 分区数据到 cos,传递完数据后,需要修改 hive 表分区的 location。此时,有个问题需要考虑,hive 分区字段的类型,支持很多种,是不是一定得 严格按照 分区字段的实际类型,去写 修改 hive 分区 location 的 alter 语句呢?如果严格需要,那么就不得不连接 hive meta 数据库,做一些多表关联,才能获取对应的 分区类型信息,sql 如下:

SELECT
  TBLS.TBL_NAME AS data_name,
  PARTITION_KEYS.PKEY_NAME AS partition_name,
  PARTITION_KEYS.PKEY_TYPE AS partition_type_name,
  ifnull(PARTITION_KEYS.PKEY_COMMENT, '') AS partition_comment,
  PARTITION_KEYS.INTEGER_IDX AS partition_position
FROM
  TBLS
  INNER JOIN DBS ON TBLS.DB_ID = DBS.DB_ID
  INNER JOIN PARTITION_KEYS ON TBLS.TBl_id = PARTITION_KEYS.TBL_ID
WHERE
  DBS.name = #{dbName}
ORDER BY
  data_name,
  partition_position ASC

使用这种方式,直接查询 hive meta 数据库,存在安全和性能风险,不是优选方案,因此,需要查看 hive 源码,研究下 alter 分区的 location 的底层实现原理。

源码分析

1.测试表 test_part 建表语句如下,包含两个 int 类型分区字段和一个string 类型的分区字段

2.远程 Debug 如下 sql
(1)year 按 int 类型的值指定
(2)month 按 string 类型指定

alter table test_part partition(year=2020, month='10', type='test') set location 'xxxx'

3.代码执行 (1)CliDriver main 方法是入口,执行的 sql 按行读取,交给 processLine 执行     DEBUG 信息

(2)processLine 方法,按照分号拆分 sql 命令,依次执行 (3)sql 会一层层传递处理(由于代码逻辑较多,此处只写和该问题相关的逻辑),最终,会调用 DDLTask 的 alterTable 方法

       DEBUG 信息

(4)通过断点,可以看到,在 hive 内部是用 String 类型保存的 partSpec 信息,所以修改分区的时候,不需要区分类型(month 本来是 int 类型的,应该写为 month=10,而不是 month='10',但是由于底层用的 string 保存,所以写成 month=10 或 month='10' 都会被保存为 month='10', 所以,这两种写法都能执行成功)

写法 1:
alter table test_part partition(year=2020, month='10', type='test') set location 'xxxx';

写法 2:
alter table test_part partition(year=2020, month=10, type='test') set location 'xxxx';

底层实际是按照 string 保存的 partSpec 信息

(5)那么这个 partSpec 哪里设置的? 传入的 sql 会被 Driver 进行 compile:首先生成 ASTNode,然后 analyze ASTNode(调用 analyzeInternal)

analyzeInternal 方法内部调用 getValidatedPartSpec

而 getValidatedPartSpec 会调用 getPartSpec 方法,读取 ASTNode 上的节点信息(保存了 分区字段),然后保存到 HashMap<String, String> 中

总结

hive 内部是用 String 类型保存的 partSpec 信息,所以写 alter 分区的 sql 语句时,语法上不区分区类型,统一用 string 是可以正常执行的(注意:分区的值,必须是对应类型的,值的类型是会被检测的,否则会抛出类型转换异常错误)