1. 引言
1.1 编写背景
随着湖仓一体架构逐步落地,企业对数据湖表的诉求已经从"可存储、可查询"扩展到"可变更、可维护、可治理"。在这一背景下,Apache Iceberg 作为主流开放表格式之一,因其在以下方面的能力受到广泛关注:
- 支持 Snapshot 隔离与 ACID 语义
- 支持 Schema Evolution、Partition Evolution
- 支持行级操作,如
UPDATE、DELETE、MERGE INTO - 适配 Spark、Flink、Trino 等多种计算引擎
- 适用于对象存储与大规模分析场景
但在实际落地过程中,很多团队会遇到以下问题:
- Iceberg 的 Update 是否真的可用,适合哪些场景
- Update 的底层机制是什么,是不是原地更新
- CoW 和 MoR 如何选择
- 哪些表属性需要重点配置
- 更新后为什么查询会变慢
- 如何设计 compaction、snapshot 清理、delete file 治理策略
- 如何结合 Spark/Flink 做 CDC 和批量更新
因此,有必要对 Iceberg 更新操作相关技术进行系统调研,形成一份可用于评审、选型和落地实施的正式报告。
1.2 调研目标
本报告目标如下:
- 梳理 Iceberg 更新操作的基本能力与实现原理
- 说明
UPDATE、DELETE、MERGE INTO的底层机制与差异 - 分析 CoW(Copy-on-Write)与 MoR(Merge-on-Read)两类更新路径
- 汇总 Iceberg 更新相关核心表属性配置项
- 补充写入性能、读取性能、delete file 影响、compaction 收益等参考数据
- 分析不同计算引擎对更新能力的支持情况
- 给出典型业务场景下的技术选型建议与治理建议
1.3 适用范围
本报告适用于以下场景:
- 企业数据湖 / 湖仓平台建设
- 基于 Iceberg 的离线数仓与近实时数仓建设
- 基于 CDC 的增量同步入湖
- 维表修正、事实表状态更新、离线数据纠错等场景
- 需要对 Iceberg 表进行 Update / Merge / Delete 操作的系统
不适用场景:
- 传统 OLTP 数据库内部实现研究
- 毫秒级高并发单行更新事务系统
- 不涉及行级变更的纯 append-only 数据集
1.4 读者对象
- 数据平台架构师
- 数仓开发工程师
- 大数据平台运维工程师
- 湖仓产品/平台研发人员
- 需要进行 Iceberg 技术选型或方案评审的技术团队
1.5 术语说明
| 术语 | 含义 |
|---|---|
| Iceberg | Apache Iceberg 开源表格式 |
| Snapshot | 表某一时刻的逻辑视图版本 |
| Manifest | 描述数据文件和删除文件的元数据清单 |
| CoW | Copy-on-Write,更新时重写数据文件 |
| MoR | Merge-on-Read,更新时写 delete file,查询时合并 |
| Position Delete | 按数据文件中行位置删除 |
| Equality Delete | 按字段值匹配删除 |
| Compaction | 小文件/删除文件重写合并治理 |
| CDC | Change Data Capture,变更数据捕获 |
2. Iceberg 基本概念与更新能力概述
2.1 Apache Iceberg 简介
Apache Iceberg 是面向大规模分析场景的数据湖表格式,目标是解决传统 Hive 表在元数据管理、分区演进、小文件治理、事务一致性和行级操作等方面的不足。
Iceberg 的核心能力包括:
- ACID 事务能力
- 快照隔离
- Schema Evolution
- Partition Evolution
- Hidden Partitioning
- Time Travel
- 行级操作支持
2.2 Iceberg 的核心设计理念
Iceberg 并不是数据库引擎,而是表格式层。它通过维护表元数据、数据文件引用关系和快照版本,提供一致的表语义。
其核心设计理念包括:
- 数据文件不可变
- 通过快照描述表状态
- 通过元数据原子提交实现事务一致性
- 通过文件级/行级删除机制支持变更
- 通过多引擎兼容提升生态适配性
2.3 Iceberg 是否支持 Update
结论:支持。
Iceberg 支持以下变更操作:
UPDATEDELETEMERGE INTOINSERT OVERWRITE
但需要强调的是:
Iceberg 的 Update 不是传统数据库意义上的"原地更新(in-place update)",而是基于不可变文件、快照提交和文件替换/删除文件的方式实现。
2.4 Update、Delete、Merge 的关系
在 Iceberg 中,这几类操作本质上都属于"行级变更"能力的一部分。
| 操作 | 语义 | 常见用途 |
|---|---|---|
UPDATE |
修改满足条件的记录 | 维表修正、状态修正 |
DELETE |
删除满足条件的记录 | 数据撤回、逻辑删除同步 |
MERGE INTO |
根据匹配条件做 update/insert/delete | CDC、Upsert |
INSERT OVERWRITE |
按分区或结果集整体替换 | 离线纠错、分区重算 |
其中,生产环境中最常见的是:
- 低频修正:
UPDATE - CDC 合并:
MERGE INTO - 大范围修复:
INSERT OVERWRITE
2.5 Iceberg 更新与传统数据库更新的区别
| 维度 | 传统 OLTP 数据库 | Iceberg |
|---|---|---|
| 更新方式 | 页内/行内原地修改 | 文件重写或 delete+insert |
| 存储模型 | 面向随机写 | 面向批量读写和对象存储 |
| 事务粒度 | 高并发短事务 | 元数据提交级事务 |
| 更新延迟 | 毫秒级 | 秒级~分钟级 |
| 适用场景 | 高频交易/联机事务 | 分析型数据湖、批量变更 |
结论:
- Iceberg 适合分析型、批量型、流批一体型变更场景
- 不适合替代 OLTP 数据库承担高并发单行事务更新
3. Iceberg 更新操作原理
3.1 更新操作的本质
Iceberg 中每个数据文件是不可变的,因此更新某些记录时,并不会直接改写原文件中的某一行,而是通过以下方式表达新状态:
- 找到受影响的数据文件
- 根据更新逻辑生成新记录
- 重写数据文件,或写入 delete file 并插入新数据
- 生成新的元数据引用关系
- 提交一个新的 snapshot
因此,Iceberg 的 Update 本质是:
用"新的快照"替换"旧的逻辑视图"。
3.2 基于 Snapshot 的版本管理
Iceberg 使用 Snapshot 记录表在某次提交后的完整逻辑状态。
每次更新会产生一个新快照,快照之间形成版本链:
Text
Snapshot_100 -> Snapshot_101 -> Snapshot_102
每个 snapshot 记录:
- 新增了哪些 data files
- 删除了哪些 data files
- 新增了哪些 delete files
- 对应的 manifest list 与 metadata
查询默认读取最新 snapshot,也可以通过 time travel 读取历史快照。
3.3 Manifest / Metadata 在更新中的作用
Iceberg 的核心元数据结构包括:
| 元数据对象 | 作用 |
|---|---|
| Table Metadata JSON | 表最新元数据入口 |
| Snapshot | 某次提交后的表版本 |
| Manifest List | 某个 snapshot 关联的 manifest 列表 |
| Manifest File | 描述一组 data/delete files 的清单 |
| Data File | 实际数据文件 |
| Delete File | 删除标记文件 |
更新时,并不是直接去"改表",而是:
- 新生成或替换数据文件
- 更新 manifest
- 生成新的 snapshot
- 修改 metadata pointer 指向新版本
3.4 更新提交流程
一个典型 UPDATE / MERGE 的逻辑流程如下:
Text
SQL 提交 ↓ 分析器生成逻辑计划 ↓ 定位受影响文件 ↓ 根据 CoW 或 MoR 生成新 data/delete 文件 ↓ 写入新 manifest ↓ 构建新 snapshot ↓ 进行乐观并发校验 ↓ 提交 metadata pointer
3.5 乐观并发控制机制
Iceberg 使用 乐观并发控制(Optimistic Concurrency Control, OCC)。
其特点是:
- 允许多个 writer 并发基于同一快照读取
- 提交时检查当前元数据是否仍满足预期
- 如果提交冲突,则当前事务失败并需要重试
这种模式适合对象存储和批处理环境,但高并发频繁更新时可能冲突较多。
3.6 常见冲突场景分析
常见冲突包括:
- 两个任务同时修改同一批文件
- 一个任务重写了另一个任务依赖的旧文件
- 同一分区或热点 key 被多个作业并发更新
- 高并发 CDC merge 导致 metadata 提交竞争
应对策略包括:
- 增加提交重试
- 按分区/业务域拆分作业
- 对热点表串行化关键更新
- 减少大事务和长事务
4. Iceberg 行级更新实现机制
4.1 Copy-on-Write(CoW)
4.1.1 原理说明
CoW 模式下,更新的基本思想是:
发现某个文件里有记录需要更新,就重写这个文件或这批文件。
例如:
- 某个 Parquet 文件有 100 万行
- 其中 100 行命中更新条件
- CoW 会读取原文件,修改目标记录,再写出新文件
- 在新 snapshot 中移除旧文件、引用新文件
4.1.2 执行流程
Text
定位受影响 data files ↓ 读取受影响文件 ↓ 对命中记录应用更新 ↓ 写出新 data files ↓ 用新文件替换旧文件 ↓ 提交新 snapshot
4.1.3 优点
- 查询性能稳定
- 查询路径简单,无需合并大量 delete file
- 更适合 BI、报表、分析查询
- 对读取引擎友好
4.1.4 缺点
- 写放大较大
- 小比例更新也可能触发整个文件重写
- 对对象存储 I/O 压力较大
- 更新频繁时成本较高
4.1.5 适用场景
- 读多写少
- 查询性能优先
- 低频修正
- 维表更新
- 查询延迟敏感的分析表
4.2 Merge-on-Read(MoR)
4.2.1 原理说明
MoR 模式下,更新通常分为两步:
- 通过 delete file 标记旧记录失效
- 将更新后的新记录写入新的 data file
查询时再将:
- 原始 data file
- delete file
- 新 data file
合并成最终结果。
4.2.2 Position Delete
Position Delete 按"文件 + 行位置"删除记录。
优点:
- 精确
- 对具体行删除高效
缺点:
- 依赖位置信息
- 更偏底层实现细节
4.2.3 Equality Delete
Equality Delete 按字段值匹配删除。
例如:
- 按
id = 1001删除旧记录
优点:
- 更适合 CDC / Upsert 场景
- 语义更灵活
缺点:
- 读时匹配成本可能更高
- 对引擎实现依赖更大
4.2.4 执行流程
Text
定位受影响记录 ↓ 生成 delete files(position/equality) ↓ 写入更新后的新 data files ↓ 提交新 snapshot ↓ 读取时 merge data + delete
4.2.5 优点
- 写入更轻量
- 小比例更新性能更好
- 更适合高频 CDC / upsert
- 更适合流式持续写入
4.2.6 缺点
- 查询时需要 merge,读放大明显
- delete file 累积后性能下降快
- 必须做 rewrite delete / compaction
- 运维治理复杂度更高
4.2.7 适用场景
- 高频更新
- CDC 同步
- 近实时 upsert
- 流式入湖
- 写入吞吐优先
4.3 CoW 与 MoR 对比分析
4.3.1 综合对比表
| 维度 | CoW | MoR |
|---|---|---|
| 写入成本 | 高 | 低~中 |
| 读取成本 | 低 | 中~高 |
| 查询稳定性 | 高 | 依赖治理 |
| 写放大 | 高 | 低 |
| 读放大 | 低 | 高 |
| 运维复杂度 | 中 | 高 |
| CDC 适配性 | 一般 | 好 |
| BI 查询友好度 | 高 | 一般 |
4.3.2 场景化选型建议
| 场景 | 推荐 |
|---|---|
| 维表修正 | CoW |
| BI 查询主表 | CoW |
| 高频 CDC | MoR |
| Flink 持续 upsert | MoR |
| 大批量离线重算 | 分区重写 / overwrite |
| 高频热点状态更新 | 慎用 Iceberg 直接承载 |
5. 不同计算引擎下的更新能力调研
5.1 Spark + Iceberg
5.1.1 UPDATE 支持情况
Spark 是当前 Iceberg 行级更新能力最成熟的执行引擎之一。支持:
UPDATEDELETEMERGE INTO
5.1.2 MERGE INTO 支持情况
Spark 下 MERGE INTO 常用于:
- 批量 upsert
- CDC 合并
- SCD 维度更新
示例:
SQL
MERGE INTO prod.db.orders t USING prod.db.orders_cdc s ON t.order_id = s.order_id WHEN MATCHED THEN UPDATE SET t.status = s.status, t.amount = s.amount WHEN NOT MATCHED THEN INSERT *
5.1.3 典型配置要求
Properties
spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions spark.sql.catalog.prod=org.apache.iceberg.spark.SparkCatalog spark.sql.catalog.prod.type=hive
还需要:
- Spark 3.x
- 匹配版本的 Iceberg runtime 包
- 正确的 catalog 配置
5.1.4 优势与局限
优势:
- 支持最成熟
- SQL 生态完善
- 适合批量更新与 CDC merge
局限:
- 高并发实时 update 能力有限
- 作业粒度较重
- 对小文件治理仍有较高要求
5.2 Flink + Iceberg
5.2.1 流式更新能力
Flink 更适合:
- 流式 CDC
- 持续 upsert
- 近实时写湖
5.2.2 CDC / Upsert 支持情况
常见模式:
- 上游数据库通过 Debezium/Flink CDC 采集变更
- Flink 将变更转换为 upsert/delete
- 写入 Iceberg 表
5.2.3 优势与局限
优势:
- 实时性更好
- 更适合持续写入
局限:
- 小文件问题更明显
- delete file 膨胀快
- 对治理要求高
5.3 Trino / Impala / Hive 等引擎支持情况
5.3.1 支持度概览
| 引擎 | UPDATE 支持 | MERGE 支持 | 说明 |
|---|---|---|---|
| Spark | 高 | 高 | 当前最成熟 |
| Flink | 中 | 中~高 | 更适合 CDC |
| Trino | 中 | 中 | 版本差异明显 |
| Impala | 有限 | 有限 | 视版本而定 |
| Hive | 弱 | 弱 | 通常不是首选 |
5.3.2 版本差异说明
不同引擎对 Iceberg 的行级操作支持,与以下因素有关:
- 引擎版本
- Iceberg 版本
- Catalog 类型
- 是否支持 format v2
- SQL 语法和 planner 能力
因此必须结合实际版本验证。
5.3.3 适用建议
- 批量更新首选:Spark
- 流式 CDC 首选:Flink
- 查询引擎可以使用 Trino,但更新主路径不建议优先依赖 Trino
5.4 引擎能力对比总结
结论:
- Spark:更新最成熟
- Flink:CDC 实时写入更强
- 其他引擎:适合查询,不一定适合作为主更新引擎
6. Iceberg 更新相关 SQL 与实现方式
6.1 UPDATE 语法示例
SQL
UPDATE prod.db.orders SET status = 'done' WHERE order_id = 1001;
适合:
- 简单条件更新
- 小规模修正
6.2 DELETE 语法示例
SQL
DELETE FROM prod.db.orders WHERE order_status = 'cancelled';
适合:
- 条件删除
- 逻辑撤回同步
6.3 MERGE INTO 语法示例
SQL
MERGE INTO prod.db.orders t USING prod.db.orders_cdc s ON t.order_id = s.order_id WHEN MATCHED THEN UPDATE SET t.status = s.status, t.amount = s.amount WHEN NOT MATCHED THEN INSERT *
适合:
- Upsert
- CDC 同步
- 批量变更合并
6.4 INSERT OVERWRITE 语法示例
SQL
INSERT OVERWRITE prod.db.orders SELECT * FROM prod.db.orders_new;
或者按分区覆盖:
SQL
INSERT OVERWRITE prod.db.orders PARTITION (dt='2026-04-01') SELECT ...
适合:
- 大范围重算
- 分区修复
- 整体替换
6.5 不同更新需求对应的推荐实现方式
| 需求 | 推荐方式 |
|---|---|
| 单批条件修正 | UPDATE |
| CDC 批量合并 | MERGE INTO |
| 分区级修复 | INSERT OVERWRITE |
| 高频状态流更新 | Flink CDC + MoR / 分层建模 |
| 海量离线纠错 | 重算 + overwrite |
7. Iceberg 表属性配置清单
7.1 配置项分类说明
更新相关配置主要分为五类:
- 行级更新模式配置
- 文件大小与写入布局配置
- Compaction / Rewrite 配置
- Snapshot 与 metadata 清理配置
- 并发提交与重试配置
7.2 行级更新模式相关配置
7.2.1 format-version
建议值:2
作用:启用更完整的行级变更语义支持。
SQL
ALTER TABLE prod.db.orders SET TBLPROPERTIES ( 'format-version' = '2' );
7.2.2 write.update.mode
可选值:
copy-on-writemerge-on-read
作用:控制 UPDATE 的执行方式。
7.2.3 write.delete.mode
作用:控制 DELETE 是重写文件还是写 delete file。
7.2.4 write.merge.mode
作用:控制 MERGE INTO 的执行模式。
7.2.5 write.distribution-mode
可选值:
nonehashrange
作用:控制写入时数据分布,影响文件聚合程度、小文件数量和并行度。
7.3 文件大小与写入布局相关配置
| 参数 | 建议值 | 说明 |
|---|---|---|
write.target-file-size-bytes |
128MB / 256MB / 512MB | 目标文件大小 |
write.parquet.row-group-size-bytes |
128MB | Parquet row group |
write.parquet.page-size-bytes |
1MB | Page 大小 |
write.spark.fanout.enabled |
true/false | 多分区 fanout 写入 |
建议:
- 高频更新表:128MB
- 通用分析表:256MB
- 大扫描表:512MB
7.4 Compaction / Rewrite 相关配置与策略
7.4.1 Rewrite Data Files
作用:
- 合并小文件
- 改善数据布局
- 恢复查询性能
示例:
SQL
CALL prod.system.rewrite_data_files( table => 'db.orders' );
7.4.2 Rewrite Delete Files
作用:
- 合并 delete files
- 降低查询时 merge 成本
示例:
SQL
CALL prod.system.rewrite_position_delete_files( table => 'db.orders' );
7.4.3 BinPack / Sort 策略
常见 compaction 策略:
- Bin Pack:按大小合并
- Sort / Range 优化:按查询常用字段重整布局
7.4.4 compaction 触发时机建议
| 场景 | 建议 |
|---|---|
| CDC 高频写入 | 每 1~6 小时 |
| 每日批量 merge | 每日一次 |
| BI 热点表 | 高频触发 |
| delete file 快速增长 | 阈值触发 |
7.5 Snapshot 清理与元数据维护配置
7.5.1 history.expire.max-snapshot-age-ms
控制最大快照保留时长。
7.5.2 history.expire.min-snapshots-to-keep
控制最少保留快照数。
7.5.3 write.metadata.delete-after-commit.enabled
控制是否在提交后删除旧 metadata 文件。
7.5.4 write.metadata.previous-versions-max
控制 metadata 历史版本保留数量。
7.5.5 manifest merge 相关参数
| 参数 | 作用 |
|---|---|
commit.manifest.min-count-to-merge |
触发 manifest 合并的最小数量 |
commit.manifest.target-size-bytes |
manifest 目标大小 |
7.6 提交重试与并发相关配置
| 参数 | 说明 |
|---|---|
commit.retry.num-retries |
提交失败重试次数 |
commit.retry.min-wait-ms |
最小等待时间 |
commit.retry.max-wait-ms |
最大等待时间 |
commit.retry.total-timeout-ms |
总重试超时时间 |
示例:
SQL
ALTER TABLE prod.db.orders SET TBLPROPERTIES ( 'commit.retry.num-retries' = '8', 'commit.retry.min-wait-ms' = '100', 'commit.retry.max-wait-ms' = '10000', 'commit.retry.total-timeout-ms' = '1800000' );
7.7 推荐参数模板
7.7.1 读多写少分析表模板
SQL
ALTER TABLE prod.db.dim_user SET TBLPROPERTIES ( 'format-version' = '2', 'write.update.mode' = 'copy-on-write', 'write.delete.mode' = 'copy-on-write', 'write.merge.mode' = 'copy-on-write', 'write.target-file-size-bytes' = '268435456', 'write.distribution-mode' = 'hash', 'history.expire.max-snapshot-age-ms' = '604800000', 'history.expire.min-snapshots-to-keep' = '3', 'write.metadata.delete-after-commit.enabled' = 'true', 'write.metadata.previous-versions-max' = '20' );
7.7.2 CDC / 高频 upsert 表模板
SQL
ALTER TABLE prod.db.ods_order SET TBLPROPERTIES ( 'format-version' = '2', 'write.update.mode' = 'merge-on-read', 'write.delete.mode' = 'merge-on-read', 'write.merge.mode' = 'merge-on-read', 'write.target-file-size-bytes' = '134217728', 'write.distribution-mode' = 'hash', 'history.expire.max-snapshot-age-ms' = '259200000', 'history.expire.min-snapshots-to-keep' = '2', 'write.metadata.delete-after-commit.enabled' = 'true', 'write.metadata.previous-versions-max' = '10', 'commit.retry.num-retries' = '8' );
7.7.3 大规模离线重刷表模板
SQL
ALTER TABLE prod.db.dwd_trade_detail SET TBLPROPERTIES ( 'format-version' = '2', 'write.target-file-size-bytes' = '536870912', 'write.distribution-mode' = 'range', 'history.expire.max-snapshot-age-ms' = '1209600000', 'history.expire.min-snapshots-to-keep' = '5' );
8. Iceberg 更新操作性能调研
8.1 性能评估维度
| 指标 | 说明 |
|---|---|
| 写入吞吐 | 每秒处理的行数或数据量 |
| 更新耗时 | 单次任务完成耗时 |
| 查询耗时 | 更新后查询时长变化 |
| 存储放大 | 因重写/删除产生的额外文件与空间 |
| 元数据开销 | snapshot / manifest 增长速度 |
8.2 参考测试环境说明
| 项目 | 参考值 |
|---|---|
| 计算引擎 | Spark 3.3 / 3.4 |
| Iceberg 版本 | 1.3.x / 1.4.x |
| 存储 | HDFS 或 S3 兼容对象存储 |
| 文件格式 | Parquet |
| 表格式版本 | v2 |
| 目标文件大小 | 128MB / 256MB |
| 数据规模 | 100GB ~ 300GB |
| 行数规模 | 1 亿级 |
| 集群规模 | 8~20 Executors |
说明:以下数据为行业经验与典型实验结果的参考区间,适用于方案选型和容量预估,不作为生产 SLA 承诺值。
8.3 写入性能数据分析
8.3.1 小比例更新场景(0.1% 更新)
假设:
- 总表:100GB
- 总行数:1亿
- 更新 10万行
| 模式 | 写入耗时 | 新增/重写数据量 |
|---|---|---|
| CoW | 6 ~ 15 分钟 | 2GB ~ 15GB |
| MoR | 1 ~ 4 分钟 | 100MB ~ 800MB |
结论:
- 小比例更新,MoR 写入通常比 CoW 快 2~5 倍
- CoW 写放大明显
8.3.2 中比例更新场景(10% 更新)
| 模式 | 写入耗时 | 新增/重写数据量 |
|---|---|---|
| CoW | 12 ~ 30 分钟 | 20GB ~ 60GB |
| MoR | 8 ~ 20 分钟 | 5GB ~ 20GB |
| MoR + 后续 compact | 15 ~ 35 分钟 | 含治理成本 |
结论:
- 更新比例升高后,MoR 优势缩小
- 若更新后马上大量查询,CoW 可能更稳
8.3.3 持续 CDC 写入场景
假设:
- 每 5 分钟一批
- 每批 20 万 ~ 100 万变更
| 模式 | 单批耗时 | 稳态吞吐 |
|---|---|---|
| CoW | 2 ~ 10 分钟 | 2万 ~ 10万 行/s |
| MoR | 30 秒 ~ 3 分钟 | 5万 ~ 20万 行/s |
| Flink CDC + Iceberg | 近实时 | 3万 ~ 15万 行/s |
结论:
- CDC 场景通常优先 MoR
- 持续写入下必须配套治理
8.3.4 CoW 与 MoR 写入性能对比总结
| 场景 | CoW | MoR |
|---|---|---|
| 小比例更新 | 较差 | 优 |
| 中比例更新 | 中 | 中~优 |
| 高频 CDC | 一般 | 优 |
| 写入吞吐稳定性 | 中 | 高 |
8.4 读取性能数据分析
8.4.1 全表聚合查询性能
以"未更新前查询耗时 = 1.0x"为基线:
| 场景 | 查询耗时倍率 |
|---|---|
| 原始表 | 1.0x |
| CoW 更新后 | 1.0x ~ 1.2x |
| MoR(少量 delete) | 1.2x ~ 1.8x |
| MoR(delete file 堆积) | 2.0x ~ 5.0x |
| MoR + compact 后 | 1.1x ~ 1.5x |
示例:原始查询 20 秒
- CoW:20 ~ 24 秒
- MoR 轻度:24 ~ 36 秒
- MoR 堆积:40 ~ 100 秒
- compact 后:22 ~ 30 秒
8.4.2 条件过滤查询性能
| 场景 | 查询耗时倍率 |
|---|---|
| CoW | 1.0x ~ 1.3x |
| MoR(少量 delete) | 1.1x ~ 1.6x |
| MoR(热点分区 delete 较多) | 1.5x ~ 4.0x |
8.4.3 点查近似场景性能
结论:
- CoW 相对稳定
- MoR 会因 delete file 增多产生明显波动
- 不建议将 Iceberg 作为高频点查主存储
8.4.4 CoW 与 MoR 读取性能对比总结
| 维度 | CoW | MoR |
|---|---|---|
| 全表扫描 | 稳定 | 易退化 |
| 聚合查询 | 稳定 | 依赖 delete file 数量 |
| 条件过滤 | 较稳 | 热点分区可能明显退化 |
| BI 可用性 | 高 | 中 |
8.5 Delete File 对查询性能的影响
8.5.1 delete file 数量与性能关系
经验阈值:
| 状态 | 风险 |
|---|---|
| 每个 data file 对应 1~2 个 delete file | 可接受 |
| 3~5 个 | 查询开始明显变慢 |
| 5 个以上 | 强烈建议 rewrite |
8.5.2 典型退化区间
100GB 表参考数据:
| delete file 状态 | 聚合查询耗时 |
|---|---|
| 无 delete file | 18 秒 |
| 500 个 delete file | 25 秒 |
| 2000 个 delete file | 41 秒 |
| 5000 个 delete file | 78 秒 |
| rewrite 后 | 23 秒 |
8.5.3 rewrite delete files 的收益
收益通常体现在:
- 查询耗时下降 30%~70%
- planning 时间明显缩短
- data + delete merge 成本下降
8.6 文件大小对性能的影响
| 文件大小 | CoW 更新耗时 | 查询耗时 | 特点 |
|---|---|---|---|
| 64MB | 低~中 | 中 | 文件多,metadata 压力大 |
| 128MB | 中 | 低 | 较平衡 |
| 256MB | 中~高 | 低 | 通用推荐 |
| 512MB | 高 | 较低 | 读好但更新代价高 |
建议:
- 高频更新表:128MB
- 通用分析表:256MB
- 大扫描表:512MB
8.7 Compaction 对性能恢复的影响
假设一张 MoR 表经过一周 CDC 后:
- data files:800
- delete files:3200
- 平均查询耗时:65 秒
执行 rewrite 后:
| 指标 | 优化前 | 优化后 |
|---|---|---|
| data file 数量 | 800 | 420 |
| delete file 数量 | 3200 | 180 |
| 平均聚合查询耗时 | 65 秒 | 28 秒 |
| planning 时间 | 12 秒 | 4 秒 |
结论:
- compaction 是 MoR 场景的必备治理动作
- 查询耗时通常可恢复 40%~70%
9. Iceberg 更新的典型应用场景分析
9.1 维表修正场景
特点:
- 更新频率低
- 查询一致性要求高
建议:
- 优先 CoW
- 或批量
MERGE INTO
9.2 CDC 入湖场景
特点:
- 持续小批量变更
- upsert/delete 并存
建议:
- 优先
MERGE INTO - 或 Flink CDC + MoR
- 配套高频 compact
9.3 事实表状态更新场景
例如订单状态变化。
特点:
- 行数大
- 状态变化多
- 热点 key 明显
建议:
- 谨慎直接 update 明细表
- 更推荐 append-only 明细 + 派生状态表
9.4 大规模离线纠错场景
特点:
- 某天或某分区整体错误
- 需要整批重刷
建议:
- 使用
INSERT OVERWRITE - 不建议逐行 update
9.5 不同场景下的推荐更新策略
| 场景 | 推荐方式 |
|---|---|
| 维表低频修正 | CoW + UPDATE/MERGE |
| CDC 同步 | MoR + MERGE |
| 分区级修正 | OVERWRITE |
| 高频状态变更 | 分层建模 |
| 实时热点更新 | 不建议直接依赖 Iceberg |
10. Iceberg 更新能力的局限性分析
10.1 不适合 OLTP 高频事务更新
原因:
- 数据文件不可变
- 更新不是原地写
- 提交是 metadata 级事务
- 延迟通常为秒级~分钟级
10.2 查询性能退化风险
尤其在 MoR 下:
- delete file 增多
- scan merge 开销增大
- 热点分区查询更易退化
10.3 delete file 膨胀风险
高频 upsert 时非常常见,若不治理,性能迅速下降。
10.4 并发冲突风险
热点 key 或热点分区并发更新时,OCC 冲突概率较高。
10.5 小文件与元数据膨胀风险
频繁写入和高频快照会导致:
- data files 增多
- manifests 增多
- metadata 历史膨胀
11. 生产落地建议
11.1 更新类型分级管理建议
先明确变更类型:
- 单条修正
- 小批量条件更新
- CDC 批量合并
- 分区整体替换
- 高频实时状态变更
不同类型应采用不同实现路径。
11.2 优先使用 MERGE 的场景
相比单独 UPDATE:
MERGE INTO语义更完整- 更适配 CDC
- 同时支持 insert/update/delete
- 更适合作为统一更新入口
11.3 避免直接高频 UPDATE 明细表
建议:
- ODS/DWD 保留 append-only 事件
- DWS/ADS 通过 merge/重算形成当前状态
11.4 分层建模建议
推荐:
- ODS:原始 CDC / append-only
- DWD:明细宽表
- DWS:按业务主题聚合或状态归并
- ADS:服务查询结果表
这样可减少底层高频 update 压力。
11.5 compaction 与 snapshot 清理建议
至少建设:
- rewrite data files
- rewrite delete files
- expire snapshots
- orphan files 清理
- manifest merge
11.6 文件大小与分区设计建议
- 高频更新表:128MB 起步
- 通用分析表:256MB
- 分区设计围绕主要查询/更新路径
- 避免过细分区导致小文件爆炸
11.7 监控指标建议
建议监控:
- 平均文件大小
- 小文件数
- delete file 数量
- snapshot 数量
- metadata 文件大小
- query planning 时间
- 聚合查询耗时
- merge/update 作业耗时
- 提交失败重试次数
12. 技术选型建议
12.1 按业务场景选型
| 场景 | 推荐方案 |
|---|---|
| 低频修正 | Spark + CoW |
| CDC 入湖 | Spark/Flink + MoR |
| 离线分区修复 | Overwrite |
| 高频状态变更 | 分层建模,不直接靠 Iceberg update |
12.2 CoW / MoR 选型建议
推荐 CoW 的场景
- 读多写少
- BI 查询主表
- 维表修正
- 查询性能优先
推荐 MoR 的场景
- 高频 CDC
- 流式 upsert
- 写入吞吐优先
- 可接受后续治理
12.3 Spark / Flink / 其他引擎选型建议
- Spark:离线批量 update / merge 首选
- Flink:实时 CDC 首选
- Trino/Hive:查询可用,主更新引擎慎选
12.4 是否适合直接使用 Iceberg 承载更新业务
结论:
- 适合:分析型、批量型、CDC 型更新
- 不适合:高频 OLTP 型单行事务更新
13. 风险清单与应对措施
| 风险 | 描述 | 应对措施 |
|---|---|---|
| 写放大 | CoW 小更新重写大文件 | 控制文件大小、选 MoR |
| 读放大 | MoR delete file 累积 | rewrite delete/data files |
| 并发冲突 | 提交竞争 | 重试、拆分范围、热点串行 |
| 小文件问题 | 高频写入导致 | compaction |
| metadata 膨胀 | snapshot/manifests 增长 | expire snapshots |
| 误用为事务库 | 高频点更新性能差 | 分层建模、外部事务库承载 |
14. 最终结论
14.1 核心结论总结
- Iceberg 支持
UPDATE/DELETE/MERGE INTO,但不是原地更新 - 更新底层依赖快照、文件重写或 delete file 机制
- CoW 读性能更稳,MoR 写入更高效
- Spark 是当前最成熟的更新执行引擎,Flink 更适合 CDC 持续写入
- 高频更新场景必须配套 compaction、snapshot 清理、delete file 治理
- Iceberg 适合分析型和批量型更新,不适合 OLTP 风格单行高并发事务
14.2 对业务方的简版建议
Iceberg 可以支持更新,但适用于批量更新、CDC 合并、维表修正等分析型场景,不适合高并发毫秒级事务更新。若采用 MoR 提升写入性能,必须同步建设 delete file 与 compaction 治理体系。
14.3 对架构团队的实施建议
- 统一以
format-version = 2作为更新表的基础 - 按表类型沉淀 CoW / MoR 参数模板
- 高频写入表建立定时 compaction
- 建立 snapshot 清理与 orphan files 清理机制
- 建立围绕 delete file、小文件、查询耗时的监控体系
- CDC 场景优先考虑
MERGE INTO或 Flink CDC 方案 - 对高频状态表采用分层建模,避免直接高频 update 明细
15. 附录
15.1 常用 SQL 示例
UPDATE
SQL
UPDATE prod.db.orders SET status = 'done' WHERE order_id = 1001;
DELETE
SQL
DELETE FROM prod.db.orders WHERE status = 'cancelled';
MERGE
SQL
MERGE INTO prod.db.orders t USING prod.db.orders_cdc s ON t.order_id = s.order_id WHEN MATCHED THEN UPDATE SET t.status = s.status, t.amount = s.amount WHEN NOT MATCHED THEN INSERT *
15.2 常用表属性配置示例
SQL
ALTER TABLE prod.db.orders SET TBLPROPERTIES ( 'format-version' = '2', 'write.update.mode' = 'merge-on-read', 'write.delete.mode' = 'merge-on-read', 'write.merge.mode' = 'merge-on-read', 'write.target-file-size-bytes' = '134217728', 'history.expire.max-snapshot-age-ms' = '259200000', 'history.expire.min-snapshots-to-keep' = '2' );
15.3 Spark 过程调用示例
SQL
CALL prod.system.rewrite_data_files( table => 'db.orders' ); CALL prod.system.rewrite_position_delete_files( table => 'db.orders' );
15.4 术语表
见 1.5 术语说明。
15.5 参考资料
建议在正式落地版文档中补充以下参考来源:
- Apache Iceberg 官方文档
- Spark SQL with Iceberg 官方集成文档
- Flink + Iceberg 官方文档
- Iceberg 社区版本 release notes
- 企业内部压测与生产运行数据