覆盖写的语义边界
INSERT OVERWRITE 的行为完全由分区条件决定:写上 PARTITION (dt='2026-10-12') 只覆盖这一个分区;写成 PARTITION (dt) 加 SELECT 里的分区列值,覆盖范围等于 SELECT 结果涉及的所有 dt;完全不写分区条件就是整表覆盖,等价于先清空再写入。因为覆盖是「先删后写」,上游一旦产出空数据,被覆盖的分区会直接变成空分区,所以覆盖类脚本普遍需要空写保护和并发互斥。
基本信息
| 静态分区覆盖 | INSERT OVERWRITE TABLE t PARTITION (dt='2026-10-12') |
|---|---|
| 动态分区覆盖 | PARTITION (dt) 加 SELECT 中的分区列,覆盖 SELECT 命中的所有分区 |
| 整表覆盖 | 不写分区条件,等价于清空后重写 |
| 并发约束 | 同一表同一分区不允许两个任务同时覆盖 |
| Spark 相关参数 | spark.sql.sources.partitionOverwriteMode,可取 static 或 dynamic |
| 空写保护 | 产出为空时跳过覆盖,避免把已有数据清成空 |
| 事务表差异 | 事务表按事务提交,失败可回滚;普通表需靠重跑修复 |
典型使用场景
- 单日重跑:只覆盖目标日期分区,其他日期的数据不受影响
- 批量补数:用动态分区一次覆盖多天,前提是先确认 SELECT 的日期范围与预期一致
- 空结果保护:上游同步失败或过滤后无数据时跳过覆盖,保住上一版数据
- 并发控制:把同一分区的覆盖任务串行化,或按分区拆分任务避免抢占
- 整表重建属于停机操作:需要提前评估,通常改用新建表再切换的方式
三种覆盖范围写法
三种覆盖范围写法
-- 安全:显式指定单个分区,影响范围可控
INSERT OVERWRITE TABLE dws_trade_store_day PARTITION (dt='2026-10-12')
SELECT store_id, order_cnt, pay_amt
FROM tmp_trade_store_day;
-- 动态分区:覆盖范围等于 SELECT 里出现的全部 dt,先确认过滤条件
INSERT OVERWRITE TABLE dws_trade_store_day PARTITION (dt)
SELECT store_id, order_cnt, pay_amt, dt
FROM tmp_trade_store_day
WHERE dt >= '2026-10-01' AND dt < '2026-11-01';
-- 危险:不写分区条件,整表被清空重写
-- INSERT OVERWRITE TABLE dws_trade_store_day SELECT * FROM tmp_trade_store_day;覆盖范围对照
| 写法 | 覆盖范围 | 风险 |
|---|---|---|
| PARTITION (dt='2026-10-12') | 单个指定分区 | 低,影响可控 |
| PARTITION (dt) 加范围过滤 | SELECT 命中的多个分区 | 中,取决于过滤条件 |
| PARTITION (dt) 无过滤 | SELECT 涉及的全部日期 | 高,可能覆盖整月 |
| 不写分区条件 | 整表 | 最高,等于清空重写 |
覆盖写的前提是上游数据可用。上游任务失败产出一批空文件时,覆盖会把本来正确的分区清成空,脚本里必须先判断行数再决定是否执行覆盖。
Spark 的 partitionOverwriteMode 设为 dynamic 时只覆盖写入数据涉及的分区,空结果不会触发任何覆盖;设为 static 则按显式分区覆盖,与 Hive 行为一致。两种模式下脚本的安全性差别很大,切换前要评估。