使用日期分区组织 Hive 表

大数据Beginner
立即练习

介绍

零售商每天都会收到新文件。之前的报表通过存储在每行中的日期筛选记录。分区表(partitioned table)还会在元数据中记录按日期组织的方式,让查询能够选择所请求日期对应的已注册数据位置。

两方面必须一致:HDFS 中的文件,以及 Hive Metastore 中的分区记录。Metastore 是保存表和分区定义的目录服务。仅仅创建目录,不会自动注册 Hive 分区。你将在注册之前先添加新一天的文件,直接观察这个区别。

本实验独立准备两天的文件和本地第三天数据。你将创建分区外部表、注册位置、读取分区列,并检查筛选查询的计划。Hive 已就绪,无需安装或依赖之前虚拟机的状态。熟悉外部表和简单日期筛选即可。请使用终端。

将文件列与分区列分开

在这一步中,你将定义日期分区表,不再在每条记录中重复日期。查看一个预置文件:

cd /home/labex/project
cat /home/labex/project/data/orders-2026-10-14.csv

文件有六列。与之前的七列 CSV 不同,这个文件没有 order_date 字段:一个文件只包含一天的订单。检查已经准备好的每日 HDFS 目录:

hdfs dfs -ls /user/labex/hive-partitions/orders

order_date=2026-10-14 这样的目录名让人容易理解组织方式。Hive 仍然需要注册分区值和位置,才能使用它。

下面的表正常声明六个文件字段,并在 PARTITIONED BY 中单独声明 order_date DATE。Hive 将从分区元数据中提供这第七个可查询列。不要在两处都声明它,也不要往这些文件里插入第七个字段。

保存并执行表定义。here-document 将 SQL 标记之间的 SQL 文本原样写入。Beeline 的 -f 执行保存的脚本;连接 URL 和用户名指向预配置的本地 Hive 服务:

cat > /home/labex/project/queries/create-partitioned-orders.sql <<'SQL'
CREATE EXTERNAL TABLE retail_partitions.orders (
  order_id STRING, customer_id STRING, product_id STRING,
  quantity INT, unit_price DECIMAL(10,2), order_status STRING
)
PARTITIONED BY (order_date DATE)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
STORED AS TEXTFILE
LOCATION 'hdfs://localhost:9000/user/labex/hive-partitions/orders'
TBLPROPERTIES ('skip.header.line.count'='1', 'external.table.purge'='false');
SQL
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -f /home/labex/project/queries/create-partitioned-orders.sql
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "DESCRIBE retail_partitions.orders;"

DESCRIBE 包含普通字段和分区信息。现在,在任何分区注册之前统计行数:

beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "SELECT COUNT(*) FROM retail_partitions.orders;" > /home/labex/project/output/before-registration.tsv
cat /home/labex/project/output/before-registration.tsv

预期为 0。表定义存在,基础目录下也有文件,但这张新表还没有分区记录。这体现了元数据边界,不代表 CSV 被删除。

注册已有的两个每日位置

在这一步中,你将把每个日期值连接到实际目录。ALTER TABLE ... ADD PARTITION 向 Metastore 添加分区记录。每个 PARTITION (...) 提供日期值,其 LOCATION 提供相应文件位置。这些是静态分区:你明确指定每个值。

将两项注册保存到同一个脚本:

cat > /home/labex/project/queries/register-first-dates.sql <<'SQL'
ALTER TABLE retail_partitions.orders ADD
PARTITION (order_date='2026-10-14') LOCATION 'hdfs://localhost:9000/user/labex/hive-partitions/orders/order_date=2026-10-14'
PARTITION (order_date='2026-10-15') LOCATION 'hdfs://localhost:9000/user/labex/hive-partitions/orders/order_date=2026-10-15';
SQL
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -f /home/labex/project/queries/register-first-dates.sql

列出已注册值并读取日期列:

beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "SHOW PARTITIONS retail_partitions.orders;" > /home/labex/project/output/registered-dates.tsv
cat /home/labex/project/output/registered-dates.tsv
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "SELECT order_id, order_date FROM retail_partitions.orders ORDER BY order_id;" > /home/labex/project/output/registered-orders.tsv
cat /home/labex/project/output/registered-orders.tsv

SHOW PARTITIONS 应包含 10 月 14 日和 15 日。查询返回 o1–o3,对应 10 月 14 日,以及 o4–o6,对应 10 月 15 日。日期来自各自注册的分区,不是隐藏的第七个 CSV 字段。

注册指向已有数据,不会复制或重写源文件。现在表中呈现全部六个订单,包括待付款和已退款记录。已付款销售报表仍然需要状态筛选。

先添加文件,再注册新日期

在这一步中,你将证明新建目录与新建分区记录是两个独立动作。查看预置的本地 10 月 16 日文件:

cat /home/labex/project/data/orders-2026-10-16.csv

创建 HDFS 目录并上传文件。这些熟悉的命令只影响存储:

hdfs dfs -mkdir -p /user/labex/hive-partitions/orders/order_date=2026-10-16
hdfs dfs -put /home/labex/project/data/orders-2026-10-16.csv /user/labex/hive-partitions/orders/order_date=2026-10-16/orders.csv

注册之前,向 Hive 查询该日期的行数:

beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "SELECT COUNT(*) FROM retail_partitions.orders WHERE order_date = DATE '2026-10-16';" > /home/labex/project/output/new-date-before.tsv
cat /home/labex/project/output/new-date-before.tsv

尽管文件已上传,预期仍为 0。添加缺失元数据,并再次查询该日期:

beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "ALTER TABLE retail_partitions.orders ADD PARTITION (order_date='2026-10-16') LOCATION 'hdfs://localhost:9000/user/labex/hive-partitions/orders/order_date=2026-10-16';"
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "SELECT order_id, customer_id, order_status, order_date FROM retail_partitions.orders WHERE order_date = DATE '2026-10-16' ORDER BY order_id;" > /home/labex/project/output/new-date-orders.tsv
cat /home/labex/project/output/new-date-orders.tsv

现在,o7/c1/paid 和 o8/c2/pending 连同 10 月 16 日出现了。Hive 能将这个分区值与上传目录关联起来。这个有意安排的前后比较,解释了新交付文件为什么可能已在 HDFS 中,却没有出现在报表里。

阅读分区筛选查询及其计划

在这一步中,你将选择一个已注册日期,并检查 Hive 如何规划请求。保存查询,在 WHERE 中使用分区列:

cat > /home/labex/project/queries/query-sales-partition.sql <<'SQL'
SELECT order_id, quantity, order_date
FROM retail_partitions.orders
WHERE order_date = DATE '2026-10-15'
ORDER BY order_id;
SQL
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -f /home/labex/project/queries/query-sales-partition.sql > /home/labex/project/output/selected-partition.tsv
cat /home/labex/project/output/selected-partition.tsv

预期为 o4/2、o5/1 和 o6/2,日期均为 10 月 15 日。这是日期选择,还不是已付款销售筛选:已退款的 o5 保留在本查询中是正确的。

EXPLAIN 描述查询,不返回数据行。它的 DEPENDENCY 选项专门显示查询将读取的表和分区。这个紧凑视图让你无需先学习执行阶段内部机制,就能检查输入选择。在相同查询前加上 EXPLAIN DEPENDENCY 并保存结果:

beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "EXPLAIN DEPENDENCY SELECT order_id, quantity, order_date
FROM retail_partitions.orders
WHERE order_date = DATE '2026-10-15'
ORDER BY order_id;" > /home/labex/project/output/partition-plan.txt
cat /home/labex/project/output/partition-plan.txt

结果是 JSON,一种由命名字段和列表组成的文本格式。找到 input_tables,它命名 retail_partitions@orders;再找到 input_partitions,其中唯一的 partitionName 以 order_date=2026-10-15 结尾。输入列表中没有另外两个日期。日期条件让 Hive 能在读取记录前选择相关的已注册分区;这称为分区裁剪(partition pruning)。小型数据只用于理解选择过程,查询耗时不适合作为有意义的性能比较。

最后,再次列出已注册分区:

beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "SHOW PARTITIONS retail_partitions.orders;"

三个日期应该都仍然注册。选择一个日期只改变查询输入选择,不会删除其他分区记录或文件。现在你同时具备存储层和目录服务层的证据,可以解释查询能看到哪些每日数据。

总结

你将文件字段与分区列分开,在 Hive 中注册每日 HDFS 位置,并观察到新目录在添加分区之前仍然不可见。随后,你选择一个日期并检查查询计划,同时保留全部日期。接下来,你将把查询结果保存到可复用的列式 ORC 表中。