增量更新前须确认源表按日期分区,否则INSERT OVERWRITE PARTITION易误删历史;分区字段必须在建表时用PARTITIONED BY显式声明,不可仅靠普通字段dt;未分区需先ADD PARTITION并配合数据写入。

增量更新前先确认数据分区是否可支持
如果源表没按日期分区(比如 dt 字段不是分区键,或根本没建分区),INSERT OVERWRITE PARTITION 就没法精准覆盖当天数据,容易误删历史。Hive/Spark SQL 中,分区字段必须是表定义里的 PARTITIONED BY 显式声明项,不能只是普通字段。
- 查分区结构:
SHOW PARTITIONS your_table,看输出是否含dt=2024-06-01这类格式 - 若无分区,先用
ALTER TABLE ... ADD PARTITION (dt='2024-06-01')补上,但注意:这不自动移动数据,需配合INSERT INTO或MSCK REPAIR TABLE - 新表建议建表时就写死:
PARTITIONED BY (dt STRING),别等上线后再改
用 INSERT OVERWRITE PARTITION 替代全量重跑
每天只刷当天聚合结果,核心是让 SQL 只读取当日新增+变更的原始数据,再聚合写入对应分区。关键在 WHERE 条件和 OVERWRITE 的组合用法。
- 原始日志表假设叫
event_log,有dt和event_time字段;聚合目标表叫daily_summary - 执行语句示例:
INSERT OVERWRITE TABLE daily_summary PARTITION (dt='2024-06-01') SELECT user_id, COUNT(*) AS pv, COUNT(DISTINCT session_id) AS uv FROM event_log WHERE dt = '2024-06-01' GROUP BY user_id;
- 别漏掉
WHERE dt = '2024-06-01'—— 即使event_log是分区表,不加这个条件,SQL 引擎可能扫全表,性能爆炸 - 如果原始数据有延迟(比如 06-01 的数据凌晨两点才落完),脚本调度时间得比业务日期晚至少 2 小时,且最好加个
MAX(event_time)校验兜底
处理“迟到数据”时避免重复计数
用户行为日志常有延迟到达,比如 06-01 的点击在 06-02 才入库。此时单纯按 dt 分区写入会导致 06-01 的统计漏算,但直接重跑又可能把同一条记录算两次。
- 方案一(推荐):原始表保留
event_time字段,聚合时用它定日期:TO_DATE(event_time)生成stat_date,再按此分组 + 写入对应分区 - 方案二:每日任务跑两遍——第一遍用
dt跑常规数据,第二遍用event_time扫描前 N 天的延迟数据,单独聚合后INSERT INTO(不是OVERWRITE)到对应分区 - 绝对不要用
INSERT OVERWRITE去覆盖多天分区,除非你明确知道所有依赖都已断开 - 验证是否重复:对比
SELECT COUNT(*) FROM daily_summary WHERE dt='2024-06-01'和前一天同口径值,突增 5% 以上就得查event_log里是否有重复event_id
聚合逻辑变更时如何安全上线
加个新指标(比如从 PV 改成加一个 avg_duration),旧分区数据不会自动更新,但新跑的任务会产出带新字段的结果。这时表结构不一致会直接报错。
- 先用
ALTER TABLE daily_summary ADD COLUMNS (avg_duration DOUBLE)扩展字段,Hive 兼容,Spark SQL 需设spark.sql.hive.caseSensitiveInferenceMode=NEVER - 扩展后首次运行,旧分区字段值为
NULL,新分区才有值 —— 如果下游强依赖该字段,得同步通知并给默认值(如COALESCE(avg_duration, 0.0)) - 千万别用
REPLACE COLUMNS,它会清空所有历史分区的字段定义,导致查询失败 - 上线前在测试环境用小日期范围(如
dt IN ('2024-06-01', '2024-06-02'))跑一遍,检查字段类型、NULL 性、数据量级是否合理

















