Hudi文件布局:COW与MOR表案例解析

一、业务场景设定

假设有一张用户订单表,按天分区,记录用户的订单状态变更:

vbnet 复制代码
表名: hudi_orders
Record Key: order_id
Precombine Key: update_ts
Partition Key: dt (订单日期)
目标文件大小: 128MB

模拟以下操作序列:

vbnet 复制代码
t1: 批量写入 1000 条新订单 (Insert)
t2: 更新其中 200 条订单状态为"已支付" (Upsert)
t3: 再写入 500 条新订单 + 更新 100 条为"已发货" (Upsert)
t4: 触发 Compaction (仅 MOR)
t5: 触发 Clean

二、COW 表文件布局演进

1.初始写入 t1:Insert 1000 条记录

less 复制代码
hudi_orders/
├── .hoodie/
│   ├── hoodie.properties
│   └── 20240101120000.commit                    ← t1 commit 完成标记
│       (instant: 20240101120000, action: commit)
│
└── dt=2024-01-01/
    ├── file_group_1/
    │   └── fg001_0_20240101120000.parquet       ← 600条记录, ~76MB
    └── file_group_2/
        └── fg002_0_20240101120000.parquet       ← 400条记录, ~51MB
  • 1000 条记录按目标文件大小拆分为 2 个 File Group
  • 每个 File Group 有且仅有一个 Base File(Parquet)
  • 文件名格式:[fileId]_[writeToken]_[instantTime].parquet

2.第二次写入 t2:Upsert 更新 200 条记录状态

scss 复制代码
hudi_orders/
├── .hoodie/
│   ├── 20240101120000.commit
│   └── 20240101130000.commit                    ← t2 commit
│
└── dt=2024-01-01/
    ├── file_group_1/
    │   ├── fg001_0_20240101120000.parquet       ← 旧版本 (600条)
    │   └── fg001_0_20240101130000.parquet       ← 新版本 (600条,含120条更新) 
    └── file_group_2/
        ├── fg002_0_20240101120000.parquet       ← 旧版本 (400条)
        └── fg002_0_20240101130000.parquet       ← 新版本 (400条,含80条更新) 

COW 核心行为:

3.第三次写入 t3:Insert 500 + Update 100

sql 复制代码
hudi_orders/
├── .hoodie/
│   ├── 20240101120000.commit
│   ├── 20240101130000.commit
│   └── 20240101140000.commit                    ← t3 commit
│
└── dt=2024-01-01/
    ├── file_group_1/
    │   ├── fg001_0_20240101120000.parquet       ← 旧版本v1
    │   ├── fg001_0_20240101130000.parquet       ← 旧版本v2
    │   └── fg001_0_20240101140000.parquet       ← 最新版本 (600+300=900条) 
    │                                              (含60条update + 300条insert写入此FG)
    ├── file_group_2/
    │   ├── fg002_0_20240101120000.parquet       ← 旧版本v1
    │   ├── fg002_0_20240101130000.parquet       ← 旧版本v2
    │   └── fg002_0_20240101140000.parquet       ← 最新版本 (400+200=600条含40条update)
    │
    └── file_group_3/                            ← 新建FileGroup (Insert溢出)
        └── fg003_0_20240101140000.parquet       ← 新文件 (0条旧数据, 纯新insert)
  • Small File Handling 机制:500 条 Insert 优先填充未满的 fg001 和 fg002
  • 当已有文件组都达到目标大小后,创建新的 fg003
  • Update 的 100 条通过索引定位到对应 File Group,触发整文件重写

4.Clean 后 t5:清理旧版本

sql 复制代码
hudi_orders/
├── .hoodie/
│   ├── 20240101120000.commit
│   ├── 20240101130000.commit
│   ├── 20240101140000.commit
│   └── 20240101150000.clean                     ← Clean action
│
└── dt=2024-01-01/
    ├── fg001_0_20240101140000.parquet           ← 只保留最新版本
    ├── fg002_0_20240101140000.parquet           ← 只保留最新版本
    └── fg003_0_20240101140000.parquet           ← 只有一个版本

Clean 规则:根据hoodie.cleaner.commits.retained(默认保留最近 10 个 commit 的文件版本),超出部分被清理。

三、MOR 表文件布局演进

1.初始写入 t1:Insert 1000 条(与 COW 相同)

arduino 复制代码
hudi_orders_mor/
├── .hoodie/
│   ├── hoodie.properties
│   └── 20240101120000.deltacommit               ← 注意是 deltacommit
│
└── dt=2024-01-01/
    ├── fg001_0_20240101120000.parquet           ← Base File, 600条
    └── fg002_0_20240101120000.parquet           ← Base File, 400条

MOR 表的 Insert 首次写入也是生成 Parquet Base File(与 COW 相同),区别在于后续 Upsert。

2.第二次写入 t2:Upsert 更新 200 条

arduino 复制代码
hudi_orders_mor/
├── .hoodie/
│   ├── 20240101120000.deltacommit
│   └── 20240101130000.deltacommit               ← t2 deltacommit
│
└── dt=2024-01-01/
    ├── fg001_0_20240101120000.parquet           ← Base File 不变!
    ├── .fg001_20240101130000.log.1_0-1-0        ← Log File (120条更新) 
    ├── fg002_0_20240101120000.parquet           ← Base File 不变!
    └── .fg002_20240101130000.log.1_0-1-0        ← Log File (80条更新) 

MOR 核心行为:

3.第三次写入 t3:Insert 500 + Update 100

sql 复制代码
hudi_orders_mor/
├── .hoodie/
│   ├── 20240101120000.deltacommit
│   ├── 20240101130000.deltacommit
│   └── 20240101140000.deltacommit               ← t3
│
└── dt=2024-01-01/
    │
    │── (FileGroup 1: fg001)
    ├── fg001_0_20240101120000.parquet           ← 原始Base (600条)
    ├── .fg001_20240101130000.log.1_0-1-0        ← t2的log (120条update)
    ├── .fg001_20240101140000.log.1_0-2-0        ← t3的log (60条update + 200条insert) 
    │
    │── (FileGroup 2: fg002)
    ├── fg002_0_20240101120000.parquet           ← 原始Base (400条)
    ├── .fg002_20240101130000.log.1_0-1-0        ← t2的log (80条update)
    ├── .fg002_20240101140000.log.1_0-2-0        ← t3的log (40条update + 150条insert)
    │
    │── (FileGroup 3: fg003 - 新建)
    └── .fg003_20240101140000.log.1_0-2-0        ← 纯insert的log (150条) 
                                                   (注: 无Base File!)
  • MOR 表的 Insert 可以直接写入 Log 文件(无需 Base File 先存在)
  • 同一 File Group 的不同 commit 产生不同的 Log 文件
  • 此时 fg001 的完整数据 = Base(600) + log_t2(120 update) + log_t3(60 update + 200 insert) = 实际 800 条有效记录

4. Compaction 执行 - t4

scss 复制代码
hudi_orders_mor/
├── .hoodie/
│   ├── 20240101120000.deltacommit
│   ├── 20240101130000.deltacommit
│   ├── 20240101140000.deltacommit
│   ├── 20240101150000.compaction.requested      ← 调度
│   ├── 20240101150000.compaction.inflight       ← 执行中
│   └── 20240101150000.commit                    ← 完成(注意变为commit)
│
└── dt=2024-01-01/
    │
    │── (FileGroup 1: fg001) - Compaction后
    ├── fg001_0_20240101120000.parquet           ← 旧Base (待Clean)
    ├── .fg001_20240101130000.log.1_0-1-0        ← 旧Log (待Clean)
    ├── .fg001_20240101140000.log.1_0-2-0        ← 旧Log (待Clean)
    ├── fg001_0_20240101150000.parquet           ← 新Base! (800条完整数据) 
    │
    │── (FileGroup 2: fg002) - Compaction后
    ├── fg002_0_20240101120000.parquet           ← 旧Base (待Clean)
    ├── .fg002_20240101130000.log.1_0-1-0        ← 旧Log (待Clean)
    ├── .fg002_20240101140000.log.1_0-2-0        ← 旧Log (待Clean)
    ├── fg002_0_20240101150000.parquet           ← 新Base! (570条完整数据)
    │
    │── (FileGroup 3: fg003) - Compaction后
    ├── .fg003_20240101140000.log.1_0-2-0        ← 旧Log (待Clean)
    └── fg003_0_20240101150000.parquet           ← 新Base! (150条)

5. Clean 后 t5:清理旧版本

ini 复制代码
hudi_orders_mor/
├── .hoodie/
│   └── ... (所有timeline instants保留)
│
└── dt=2024-01-01/
    ├── fg001_0_20240101150000.parquet           ← 干净的最新Base
    ├── fg002_0_20240101150000.parquet
    └── fg003_0_20240101150000.parquet
相关推荐
珠***格3 小时前
四可装置核心技术:高精度采集、边缘计算、协议自适应
大数据·人工智能·分布式·能源·边缘计算
狒狒热知识4 小时前
赋能线下实体数字化转型178软文网GEO运营助力突破地域客源限制
大数据·人工智能
汉知宝科技4 小时前
知识产权管理中的流程自动化:从人工操作到系统智能
大数据·运维
智慧景区与市集主理人4 小时前
巨有科技乡村农文旅景区智慧建设|轻量化升级,激活乡村文旅长效增收动能
大数据·科技
05候补工程师4 小时前
【期末/408冲刺】软件工程核心考点与大题通关秘籍(附图解与解题套路)
大数据·hadoop·经验分享·笔记·软件工程
AI智图坊4 小时前
电商详情页转化率提升实战:基于AI的“卖点-视觉”转化链路设计与自动化实现
大数据·运维·人工智能·gpt·ai作画·自动化·aigc
chushiyunen4 小时前
elasticsearch查询相关
大数据·elasticsearch·搜索引擎
段一凡-华北理工大学5 小时前
工业领域的Hadoop架构学习~系列文章16:实时流处理架构 - 工业数据的实时动脉
大数据·数据仓库·hadoop·分布式·学习·架构·高炉炼铁
yyuuuzz5 小时前
aws亚马逊云服务的基础认知与常见场景
大数据·运维·服务器·网络·云计算·aws