一、引言
在日常使用Hudi表进行读写操作时,一张 Hudi 表通常面临多种并发操作:流式写入/批量 ETL/表服务(Compaction、Clustering、Clean 等),如果没有合理的并发控制机制,就会导致:
- 数据丢失:写者之间互相覆盖对方的提交
- 数据不一致:部分写入成功、部分失败导致脏数据
- 元数据损坏:Timeline 元数据出现冲突导致表不可用
Hudi 1.x 版本在并发控制(Concurrency Control)方面进行了重大重构,引入了全新的 Non-Blocking Concurrency Control(NBCC)机制,相比 0.x 版本基于 Optimistic Concurrency Control(OCC)的方案,在吞吐量、冲突检测粒度和系统可用性方面都有显著提升。
二、Hudi 并发控制整体架构

关键组件说明:
| 组件 | 职责 |
|---|---|
| Lock Provider | 提供分布式锁能力,确保关键操作的互斥性 |
| Conflict Resolution Strategy | 定义冲突检测与解决策略(OCC 或 NBCC) |
| Timeline | Hudi 元数据核心,记录所有操作的时间线 |
| Transaction Manager | 协调事务的开始、提交与回滚 |
三、Hudi 0.x 的并发控制:Optimistic Concurrency Control (OCC)
Hudi 0.x 版本采用的是经典的乐观并发控制(OCC)策略,其核心思想是:假设冲突发生的概率较低,允许多个写者并行执行写入操作,但在提交阶段进行冲突检测,如果检测到冲突则中止(abort)其中一个事务。

在 0.x 版本中,OCC 冲突检测的粒度是 File Group 级别:
- 如果两个写者修改了相同的 File Group,则认为产生冲突
- 冲突检测通过比较
WriteStatus中涉及的文件列表来实现
OCC的核心配置:
ini
# 启用乐观并发控制
hoodie.write.concurrency.mode=optimistic_concurrency_control
# 锁提供者配置
hoodie.write.lock.provider=org.apache.hudi.client.transaction.lock.ZookeeperBasedLockProvider
hoodie.write.lock.zookeeper.url=zk-host:2181
hoodie.write.lock.zookeeper.port=2181
hoodie.write.lock.zookeeper.base_path=/hudi/locks
# 冲突解决策略
hoodie.write.conflict.resolution.strategy=org.apache.hudi.client.transaction.SimpleConcurrentFileWritesConflictResolutionStrategy
# 锁相关超时配置
hoodie.write.lock.wait_time_ms=5000
hoodie.write.lock.num_retries=3
OCC的局限性:
| 局限 | 说明 |
|---|---|
| 写入阻塞 | 提交阶段需要获取全局锁,多写者排队提交 |
| 冲突粒度粗 | File Group 级别冲突检测,可能产生不必要的 abort |
| Table Service 互斥 | Compaction/Clustering 与 Ingestion 容易冲突 |
| 高并发下吞吐下降 | 冲突率上升导致大量事务重试 |
| 缺乏 early conflict detection | 冲突只在提交阶段才被发现,前置写入工作浪费 |
四、Hudi 1.x 的并发控制:Non-Blocking Concurrency Control (NBCC)
Hudi 1.x引入了Non-Blocking Concurrency Control(NBCC)机制,核心思想借鉴了 MVCC(多版本并发控制)的理念:每个写者独立写入数据到各自的 File Slice,不同写者对同一记录的更新形成多个版本,由读取端或 Compaction 进行合并解决。

在 NBCC 模式下,每个 Writer 拥有独立的写入路径:
- 每个 Writer 写入到各自的 Log File(MOR 表)或新的 File Slice
- Writer 之间无需相互等待或检查对方的写入状态
- 提交操作不再需要获取全局互斥锁
基于 Ordering Field 的冲突解决:

NBCC 的一大优势是 Table Service(特别是 Compaction)与 Ingestion 不再互斥:

NBCC的核心配置:
ini
# Hudi 1.x 启用 Non-Blocking Concurrency Control
hoodie.write.concurrency.mode=NON_BLOCKING_CONCURRENCY_CONTROL
# MOR 表类型(NBCC 推荐使用 MOR)
hoodie.datasource.write.table.type=MERGE_ON_READ
# 必须配置 precombine field 用于冲突解决
hoodie.datasource.write.precombine.field=ts
# 锁提供者(NBCC 仍需要锁来保护 Timeline 元数据操作)
hoodie.write.lock.provider=org.apache.hudi.client.transaction.lock.InProcessLockProvider
# 对于多进程/多集群场景使用分布式锁
# hoodie.write.lock.provider=org.apache.hudi.client.transaction.lock.ZookeeperBasedLockProvider
NBCC对Hudi表类型的适配情况:
| 表类型 | NBCC 支持情况 | 说明 |
|---|---|---|
| MOR (Merge-On-Read) | ✅ 完全支持 | 多写者各自追加 Log File,天然适配 |
| COW (Copy-On-Write) | ⚠️ 有限支持 | COW 需要重写整个 Parquet 文件,并发写同一 File Group 仍可能冲突 |
五、0.x 与 1.x 版本并发控制对比
核心差异对比:

一致性模型差异:
| 一致性维度 | OCC (0.x) | NBCC (1.x) |
|---|---|---|
| 写入一致性 | 强一致(提交后即可见) | 最终一致(需 Compaction 合并后可见完整语义) |
| 读取一致性 | 快照隔离(Snapshot Isolation) | 快照隔离(通过 Timeline 保证) |
| 跨写者顺序 | 由提交锁保证全局顺序 | 由 ordering field 保证逻辑顺序 |
六、并发控制选型实践

Flink 多流写入同一 MOR 表的NBCC配置参考:
ini
# === 表级别配置 ===
hoodie.datasource.write.table.type=MERGE_ON_READ
hoodie.datasource.write.precombine.field=event_time
hoodie.datasource.write.recordkey.field=id
hoodie.datasource.write.partitionpath.field=dt
# === 并发控制配置 ===
hoodie.write.concurrency.mode=NON_BLOCKING_CONCURRENCY_CONTROL
# === 锁配置 (生产使用 ZooKeeper/HiveMetaStore) ===
hoodie.write.lock.provider=org.apache.hudi.client.transaction.lock.ZookeeperBasedLockProvider
hoodie.write.lock.zookeeper.url=zk1:2181,zk2:2181,zk3:2181
hoodie.write.lock.zookeeper.port=2181
hoodie.write.lock.zookeeper.base_path=/hudi/locks
hoodie.write.lock.zookeeper.lock_key=my_table
# === Compaction 配置 (与 Ingestion 独立调度) ===
hoodie.compact.inline=false
hoodie.compact.schedule.inline=true
# 异步 Compaction 由独立作业执行