写入模式概览:COW 与 MOR 模式的区别、适用场景

核心原理:两种模式如何组织数据文件

Apache Hudi 提供两种核心表类型(Table Type),即 Copy-on-Write(COW,写时复制) 与 Merge-on-Read(MOR,读时合并)。它们决定了数据在存储层如何被写入、更新与读取,也直接影响了查询延迟、写入放大与文件管理策略。

COW 模式的核心思想是“更新即重写”。每次对已有记录执行更新或删除时,Hudi 会找到包含该记录的所有基础文件(Base File,Parquet 格式),将新数据与旧数据合并后生成一批新的基础文件,并原子性地替换旧文件。因此,COW 表在任何时刻读取到的都是最新且完整的数据,无需额外合并过程。

MOR 模式则采用“增量日志 + 基础文件”的组合。更新记录首先被追加写入 Avro 格式的日志文件(Log File),而基础文件保持原样。查询时,系统需要将基础文件与日志文件进行合并,以呈现最新视图。MOR 表可以选择两种查询类型:读优化查询(Read Optimized) 只读取基础文件,返回快照数据但可能滞后;快照查询(Snapshot Query) 则实时合并日志与基础文件,返回最新数据。

关键差异速览:

  • 写入路径:COW 直接重写 Parquet 文件;MOR 先写 Avro 日志,后台再异步压缩(Compaction)成 Parquet。
  • 读取路径:COW 只读 Parquet;MOR 读优化查询只读 Parquet,快照查询需合并 Parquet + Avro。
  • 写入放大:COW 写入放大高(每次更新都重写整个文件);MOR 写入放大低(顺序追加日志)。
  • 查询延迟:COW 查询延迟稳定;MOR 快照查询可能因合并产生额外开销。
Hudi 写入模式对比:COW vs MOR COW(写时复制) 写入/更新请求 读取旧文件 合并并重写 Parquet 新基础文件(Parquet) 原子替换旧文件 查询:直接读 Parquet ✔ 查询延迟低,无合并开销 ✔ 文件数量少,易于管理 ✘ 写入放大高,频繁更新代价大 ✘ 不适合高吞吐写入场景 MOR(读时合并) 写入/更新请求 追加写入 Avro 日志文件 日志文件(.log) 记录增量更新 基础文件(Parquet) 保持不变 合并 读优化:只读 Parquet 快照:合并 Parquet + Log ✔ 写入吞吐高,追加写延迟低 ✔ 适合频繁更新、实时写入 ✘ 快照查询有合并开销 ✘ 文件数量多,需压缩管理 后台压缩(Compaction):将日志文件合并到新基础文件 MOR 表周期性执行,控制日志文件无限增长

方法流程:COW 与 MOR 的写入与查询路径

理解两种模式的最佳方式,是追踪一条数据从写入到被查询的完整路径。

COW 写入流程:

  1. 客户端发起 upsert(更新插入)操作,指定目标分区。
  2. Hudi 索引机制定位记录所在的基础文件(File Group)。
  3. 读取旧基础文件,将新记录与旧记录合并,生成新版本的基础文件。
  4. 提交(Commit)新文件版本,并原子性地将旧文件标记为删除。
  5. 查询时直接扫描最新版本的基础文件,无需任何额外合并。

MOR 写入流程:

  1. 客户端发起 upsert,索引定位目标 File Group。
  2. 更新记录以追加方式写入该 File Group 的 Avro 日志文件,不触碰基础文件。
  3. 写入操作快速返回,日志文件持续增长。
  4. 后台压缩任务定期将日志文件合并到新的基础文件,并清理旧日志。
  5. 查询时:读优化查询只扫描基础文件;快照查询则动态合并基础文件与日志文件。

实操建议:在 Hudi 中创建表时,通过 TABLE TYPE 属性指定模式:

-- COW 表
CREATE TABLE cow_table (...) USING HUDI
TBLPROPERTIES ('type' = 'COPY_ON_WRITE');

-- MOR 表
CREATE TABLE mor_table (...) USING HUDI
TBLPROPERTIES ('type' = 'MERGE_ON_READ');

案例与场景:如何选择正确的模式

选择 COW 还是 MOR,本质上是在查询延迟与写入吞吐之间做权衡。以下典型场景可作参考:

场景特征 推荐模式 原因
数据仓库 ODS 层,批量导入后较少更新 COW 写入频率低,查询性能最优,文件管理简单
用户画像、订单状态频繁变更 MOR 高频率 upsert 场景,避免反复重写大文件
近实时数据分析,要求秒级数据可见 MOR(快照查询) 日志追加后立即可见,无需等待压缩
下游使用 Presto/Spark 做批量报表 COW 读优化查询无合并成本,扫描效率高
数据湖中存储海量 IoT 传感器数据 MOR 写入吞吐要求高,且更新集中在最近分区

案例:电商订单表

某电商平台维护订单状态表,每天新增约 5000 万条订单,同时有约 10% 的订单会经历状态流转(待支付→已支付→已发货→已完成)。如果采用 COW 模式,每次状态更新都需要重写包含该订单的整个 Parquet 文件,导致严重的写入放大。改用 MOR 模式后,状态更新以日志追加方式写入,写入吞吐提升约 3 倍,同时通过每 30 分钟一次的压缩任务控制日志大小,查询侧使用快照模式保证数据实时性。

误区与复盘:常见理解偏差

误区一:MOR 一定比 COW 快

这是最常见的误解。MOR 只在写入路径上更快,但快照查询需要合并日志与基础文件,查询延迟可能比 COW 高 2~5 倍。如果业务以读为主且更新不频繁,COW 反而整体性能更优。

误区二:COW 不支持增量查询

两种模式都支持增量查询(Incremental Query),区别在于 COW 的增量数据来自新提交的基础文件版本,而 MOR 的增量数据来自日志文件。COW 的增量查询粒度受限于提交频率,MOR 则可以提供更细粒度的变更流。

误区三:MOR 不需要关心文件数量

如果压缩策略配置不当,MOR 表的日志文件会无限增长,导致快照查询性能急剧下降。必须设置合理的压缩触发策略(如基于时间、日志大小或记录数),并监控 File Group 的数量。

复盘建议:在技术选型时,不要仅凭“写入频繁就选 MOR”的简单规则。建议先用业务数据做压测:模拟真实 upsert 比例、查询频率和延迟要求,分别测量两种模式在 COW 与 MOR 下的写入吞吐、查询 P95 延迟以及存储成本,再做出最终决策。同时,Hudi 支持表类型在后续版本中通过 ALTER TABLE 进行转换,但转换过程涉及全表重写,成本较高,因此初期选型仍需谨慎。