hive里如何实现merge

在 Hive 中实现 MERGE(即 UPSERT:存在则更新,不存在则插入)主要有以下几种方式,具体选择取决于你的 Hive 版本表类型

1. 原生 MERGE 语句(推荐,Hive 2.2+ / 3.x+)

从 Hive 2.2 开始引入了标准的 SQL MERGE INTO 语法,但前提是目标表必须是 ACID 事务表

前置条件
  • Hive 版本 >= 2.2
  • 目标表必须是 ORC 格式 + 分桶表 + 事务表
  • 需开启相关配置
sql 复制代码
-- 开启ACID事务支持
SET hive.support.concurrency = true;
SET hive.enforce.bucketing = true;
SET hive.exec.dynamic.partition.mode = nonstrict;
SET hive.txn.manager = org.apache.hadoop.hive.ql.lockmgr.DbTxnManager;
建表示例
sql 复制代码
CREATE TABLE target_table (
    id INT,
    name STRING,
    amount DECIMAL(10,2)
)
CLUSTERED BY (id) INTO 4 BUCKETS
STORED AS ORC
TBLPROPERTIES ('transactional'='true');
MERGE 语法
sql 复制代码
MERGE INTO target_table t
USING source_table s
ON t.id = s.id
WHEN MATCHED AND s.is_deleted = true THEN DELETE
WHEN MATCHED THEN UPDATE SET t.name = s.name, t.amount = s.amount
WHEN NOT MATCHED THEN INSERT VALUES (s.id, s.name, s.amount);

⚠️ 注意: ACID 表的 MERGE 底层会产生 delta 文件,需要定期执行 ALTER TABLE ... COMPACT 进行合并压缩,否则查询性能会严重下降。


2. INSERT OVERWRITE 模拟 MERGE(最通用,适用于非事务表)

对于普通外部表/内部表 (Parquet、TextFile 等),无法使用原生 MERGE,通常用 INSERT OVERWRITE + FULL OUTER JOINLEFT JOIN 来模拟。这是生产环境中最常见的做法。

核心思路

将源表和目标表做关联,通过 COALESCE / CASE WHEN 决定最终取值,然后覆盖写回目标表(或新分区)。

sql 复制代码
INSERT OVERWRITE TABLE target_table PARTITION (dt='2026-06-30')
SELECT
    COALESCE(s.id, t.id)       AS id,
    -- 匹配到则取源表值,未匹配到保留目标表原值
    CASE
        WHEN s.id IS NOT NULL THEN s.name
        ELSE t.name
    END                        AS name,
    CASE
        WHEN s.id IS NOT NULL THEN s.amount
        ELSE t.amount
    END                        AS amount
FROM target_table t
FULL OUTER JOIN source_table s
    ON t.id = s.id
WHERE t.dt = '2026-06-30';     -- 只处理目标分区
变体:仅处理增量数据(性能优化)

如果源表只是增量数据,可以先 UNION 再 GROUP BY 去重:

sql 复制代码
INSERT OVERWRITE TABLE target_table PARTITION (dt='2026-06-30')
SELECT id, name, amount
FROM (
    SELECT id, name, amount,
           ROW_NUMBER() OVER (PARTITION BY id ORDER BY update_time DESC) AS rn
    FROM (
        SELECT * FROM target_table WHERE dt = '2026-06-30'
        UNION ALL
        SELECT * FROM source_table
    ) combined
) ranked
WHERE rn = 1;

💡 优点: 不依赖 ACID,兼容所有存储格式和 Hive 版本。 💡 缺点: 每次都是全量重写整个分区,数据量大时 I/O 开销高。


3. 各方案对比总结

维度 原生 MERGE (ACID) INSERT OVERWRITE 模拟
Hive 版本要求 ≥ 2.2 任意版本
表格式要求 必须 ORC + 分桶 + 事务属性 任意格式(Parquet/ORC/CSV等)
写入方式 增量写入 delta 文件 全量覆盖分区
DELETE 支持 ✅ 原生支持 ❌ 需额外过滤逻辑
查询性能 需 Compact 后才好 写完即可查,无额外维护
并发安全 ✅ 有锁机制 ❌ 无锁,需调度层保证
适用场景 实时/准实时 CDC 入仓 离线 T+1 批量 ETL
运维复杂度 高(需管理 Compaction)

💡 最佳实践建议

  1. 离线数仓首选方案 2 :大多数离线 ETL 场景不需要 ACID 特性,INSERT OVERWRITE 更稳定、更易排查问题,且与 Spark/Flink 生态兼容性更好。
  2. CDC / 实时同步选方案 1 :如果需要频繁小批量 UPSERT 且对延迟敏感,使用 ACID 表的 MERGE,但务必配置自动 Compaction 或定时手动触发。
  3. 考虑替代引擎 :如果 MERGE 是高频操作,建议评估 Apache Iceberg / Hudi / Delta Lake 等数据湖格式,它们原生支持高效 Upsert/Merge,且无需 Hive ACID 的 Compaction 负担,已成为现代数据架构的主流选择。
  4. 避免大表全量 OVERWRITE :如果目标表非常大且只有少量变更,可考虑按主键范围拆分为多个子任务并行处理,或使用分桶表配合 INSERT INTO 追加 + 读取时去重的策略。