5.1 LOAD与INSERT:两条搬运路径


5.1 LOAD 与 INSERT:两条搬运路径

本节摘要:把数据装进 Hive 有两条机理完全不同的路:LOAD 是物理搬运,把文件原样移进表目录,不生成计算任务;INSERT 是"查询加落表",SELECT 部分被完整翻译成执行计划,结果按目标表布局写出。本节对比两条路径、展开动态分区写入的参数体系、多表插入的复用技巧,并清点写入路径的四个高频故障。

先做一个决定:搬文件还是造数据

面对"把这批数据装进 orders 表"的需求,第一问是:数据现在长什么样,目标表要它长什么样?

场景一:上游给了一个已经按逗号分隔、字段顺序与表声明完全一致的文本文件。用 LOAD:

LOAD DATA LOCAL INPATH '/staging/orders_20260116.csv' INTO TABLE mall.orders PARTITION (dt = '2026-01-16');

LOCAL 表示从客户端所在机器上传(实际是复制到 HDFS),去掉 LOCAL 则要求源路径已在 HDFS 上。执行后 EXPLAIN 不了——LOAD 不产生执行计划,它是 Driver 直接发起的 HDFS 文件操作:同文件系统内是改名,跨文件系统是复制上传。秒级完成,字节不变。

正因为字节不变,LOAD 的三不限也来了:不检查分隔符是否与 SerDe 声明一致、不检查列数、不检查类型。脏文件照单全收,问题延迟到查询时爆发放大(读时模式的完整代价在 DML 层的第一现场)。生产上 LOAD 的纪律是:上游文件过质检(行数、列数、抽样值域),或者干脆只 LOAD 进 STRING 列的贴源层,清洗后再 INSERT 到正式表。

场景二:数据在另一张表里,需要清洗、转换、重分布后落位。用 INSERT:

INSERT OVERWRITE TABLE mall.orders PARTITION (dt = '2026-01-16') SELECT order_id, customer_id, region, amount FROM staging.orders_new WHERE order_id IS NOT NULL;

这条语句被翻译成一个完整的执行计划:TableScan 加 Filter 加 Select,末端是 File Output Operator 按目标表的格式(ORC)与分区布局写出。INSERT 的本质是"查询",落表只是查询的输出方向——这也是为什么 INSERT 的性能优化与第 6 章的查询优化是同一套方法论。

静态与动态分区写入

静态分区的分区值写死在 PARTITION 子句里,一条语句一个分区。动态分区的值来自 SELECT 列(第 4.2 节讲过语法),这里从计划视角补一刀:

SET hive.exec.dynamic.partition = true; SET hive.exec.dynamic.partition.mode = nonstrict; INSERT OVERWRITE TABLE mall.orders PARTITION (dt, region) SELECT order_id, customer_id, amount, dt, region FROM staging.orders_full;

执行计划里,动态分区在末端多出一个动态分区写出器:每行按末尾两列的值路由到"对应目录的对应文件"。分区清单不再由你在语句里枚举,而是运行时涌现——目录是数据自己长出来的。参数体系值得记全:

参数 作用 默认 生产建议
hive.exec.dynamic.partition 总开关 true(新版本) 确认开启
hive.exec.dynamic.partition.mode strict 要求至少一个静态列 strict 批任务用 nonstrict 前先评估分区数
hive.exec.max.dynamic.partitions 单节点最大动态分区数 1000 按分区总数上调
hive.exec.max.dynamic.partitions.pernode 单任务单节点上限 100 防爆炸的最后一道闸

动态分区的两个坑再强调一次:分区值含非法字符(路径里的斜杠与冒号)会直接报错;NULL 分区值会落进特殊的分区名,统计时容易漏。写入前对分区列做清洗与判空,是脚本的标准前置。

多表插入:一次扫描,多路落表

同一份明细,既要按天入订单表,又要按客户入宽表,两条 INSERT 就扫两遍源表。多表插入把扫描共享:

FROM staging.orders_full o INSERT OVERWRITE TABLE mall.orders PARTITION (dt) SELECT order_id, customer_id, amount, dt WHERE region IS NOT NULL INSERT OVERWRITE TABLE mall.customer_wide PARTITION (dt) SELECT customer_id, count(*), sum(amount), dt GROUP BY customer_id, dt;

FROM 前置声明公共数据源,后面挂多条 INSERT。执行计划的特征是一个共享的 TableScan 分叉出两棵输出子树——扫描一次,两路计算各自落表。对贵重的源表(大扫描或多表连接的中间结果)收益明显;对小表收益有限,反而增加计划复杂度。EXPLAIN 里看到共享扫描的分叉结构,就确认了复用生效。

INTO 还是 OVERWRITE

INSERT INTO 追加写,新文件不断堆进分区目录;INSERT OVERWRITE 覆盖写,先清目标分区再写。批处理的标准姿势是 OVERWRITE——失败重跑得到幂等结果(重跑不会翻倍数据),这是任务可重放性的基石。代价是覆盖期间分区短暂不可见,下游若并发读会撞见空窗,重要分层表的刷新要安排在下游拉数之前。

追加写的陷阱是小文件:每次 INTO 生成至少一个新文件,高频小批量写入很快把目录塞满碎片,NameNode 内存与扫描启动开销一起恶化。对策两条路线:写入侧攒批(小文件合并进一次写入),存储侧定期合并(第 8 章的分区合并治理)。

写入路径故障速查

症状 常见原因 处置方向
LOAD 成功查询全 NULL 分隔符或列序与表声明不符 贴源层清洗重建 或修 SerDe 声明
动态分区报非法分区值 值里含路径非法字符或为 NULL 写入前清洗分区列
写入巨慢 文件数暴涨 任务数乘分区数的小文件放大 收敛 Reduce 数 攒批写入 事后合并
ORC 表写进文本内容 用 LOAD 把文本文件搬进 ORC 表 LOAD 只认目录不认格式 用 INSERT 转换
覆盖后下游读空 OVERWRITE 清分区的窗口期 调度上错峰 或用版本目录切换

最后一条值得展开一句:LOAD 能把文本文件搬进声明为 ORC 的表目录(Hive 只管文件在不在目录里),之后查询按 ORC 解析必然失败。表声明与文件事实的一致性,Hive 不替你把关——这是"读时模式"在写入侧的镜像。

本节要点回顾

  • 两物种:LOAD 是 HDFS 文件操作零计算,INSERT 是完整执行计划加落表输出;
  • LOAD 三不限:不验分隔符不验列数不验类型,质检在上游或贴源层;
  • 动态分区参数族:总开关、mode、两级上限,防分区爆炸的三道闸;
  • 多表插入:共享扫描分叉输出,贵重源表的复用利器,EXPLAIN 认分叉结构;
  • OVERWRITE 换幂等:批任务标准姿势,注意覆盖窗口期的下游空读;
  • 声明与事实分离:文件进目录不等于格式正确,一致性要靠流程保证。

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U