流式数据湖Paimon探秘之旅 (六) 提交流程与事务保证

第6章:提交流程与事务保证

如何确保原子提交?

Paimon使用两阶段提交协议确保分布式写入的一致性:

scss 复制代码
阶段1:Prepare(准备)
  ├─ 验证数据(检查schema, 类型等)
  ├─ 生成临时元数据
  └─ 返回CommitMessage给用户

阶段2:Commit(提交)
  ├─ 使用分布式锁
  ├─ 冲突检测
  ├─ 原子写入Snapshot
  └─ 释放锁

提交的关键步骤

6.1 两阶段提交协议

阶段1:Prepare(数据端)

在Flink中,prepare通常发生在:

  • Checkpoint触发时
  • 应用关闭时
java 复制代码
// 写数据
write.write(record1);
write.write(record2);
write.write(record3);

// Prepare:生成元数据,但不提交
CommitMessage msg = write.prepareCommit();

msg包含:
  ├─ 生成的数据文件列表
  ├─ 文件的元数据(min_key, max_key, row_count)
  ├─ 操作类型(ADD/DELETE等)
  └─ 其他统计信息
阶段2:Commit(协调端)

Commit通常由协调器(Flink JobManager)调用:

java 复制代码
// 收集所有并行度的CommitMessages
List<CommitMessage> messages = [msg1, msg2, msg3, ...];

// 提交
commit.commit(checkpointId, messages);
为什么需要两阶段?
yaml 复制代码
单阶段提交的问题:
  Task1: 写入文件 → 立即提交 ✓
  Task2: 写入文件 → 立即提交 ✓
  Task3: 写入文件 → 提交失败 ✗
  
  结果:表中只有Task1和Task2的数据,数据丢失!

两阶段提交的优势:
  Task1: 写入文件 → 等待 (Prepare)
  Task2: 写入文件 → 等待 (Prepare)
  Task3: 写入文件 → 等待 (Prepare)
  
  协调器: 检查所有Task准备完毕 → 全部Commit
  
  如果Task3失败,Coordinator可以:
    - 回滚Task1和Task2
    - 重新启动全部Task
  
  结果:要么全部成功,要么全部失败(原子性)

6.2 FileStoreCommit提交实现

提交的步骤
markdown 复制代码
1. 获取分布式锁
   ↓
2. 检查冲突
   ├─ 读取最新Snapshot
   ├─ 比较新增文件与现有文件
   └─ 检测是否有冲突
   ↓
3. 生成新Manifest
   ├─ 合并旧Manifest + 新文件
   └─ 写入新Manifest文件
   ↓
4. 生成ManifestList
   └─ 更新清单列表
   ↓
5. 原子写入Snapshot
   └─ 写入snapshot-N文件(原子操作)
   ↓
6. 释放锁
冲突检测的原理
markdown 复制代码
情况1:无冲突(两个writer在不同bucket)
  Writer1: 写入 bucket-0 的文件
  Writer2: 写入 bucket-1 的文件
  ✓ 完全独立,无冲突

情况2:冲突(两个writer在同一bucket)
  Writer1: 写入 bucket-0 的文件A
  Writer2: 也想写入 bucket-0
  
  检测过程:
    - 读当前Snapshot → manifest-5
    - 检查bucket-0的现有文件 → [file1, file2]
    - Writer1要添加fileA → [file1, file2, fileA] ✓
    - Writer2也要添加fileB
    - 但fileA和fileB都写了user_id=100的数据
    - 冲突!✗ 需要重试或合并

6.3 Manifest文件合并

Manifest的演化
scss 复制代码
时刻1:初始状态
  snapshot-1 → manifest-1 → [file-1, file-2]

时刻2:Writer1提交
  新Manifest包含:[file-1, file-2] + [file-3] = manifest-2
  snapshot-2 → manifest-2

时刻3:Writer2提交(可能与Writer1并发)
  读最新Snapshot-2的manifest-2 → [file-1, file-2, file-3]
  新Manifest包含:[file-1, file-2, file-3] + [file-4] = manifest-3
  snapshot-3 → manifest-3
避免Manifest爆炸

多次写入会产生很多Manifest文件,需要定期合并:

css 复制代码
定期任务:CompactManifest
  
  before: manifest-1, manifest-2, manifest-3, ..., manifest-100
          (100个文件,查询时需要遍历所有)
  
  after: manifest-compact-1
         (1个合并文件,查询快速)
  
  执行命令:
  CALL compact_manifests('db', 'table');

6.4 冲突检测与乐观锁

乐观锁的思想
markdown 复制代码
假设冲突很少发生:

尝试提交:
  1. 读最新版本 (不加锁)
  2. 准备新数据
  3. 提交时加锁
  4. 验证版本未改变
  5. 写新版本
  6. 释放锁

好处:
  - 大多数写入不需要等待锁
  - 只在冲突时重试
版本号机制
java 复制代码
Snapshot {
  id: 5,                        // 快照ID
  commitIdentifier: 1673088000, // 版本号
  baseManifestList: "...",
  deltaManifestList: "..."
}

提交流程:
  预期版本:currentSnapshotId = 5
  ↓
  写入数据...
  ↓
  检查:当前仍是snapshot-5吗?
    ✓ 是 → 提交新snapshot-6
    ✗ 否 → 有其他writer已提交 → 需要重试

总结:事务保证的关键点

1. 原子性(Atomicity)

scss 复制代码
Snapshot文件的原子写入
  ↓
要么整个snapshot-N写入成功
要么完全不写(故障回滚)
  ↓
不可能出现部分更新的状态

2. 一致性(Consistency)

复制代码
冲突检测
  ↓
如果发现冲突,自动重试或失败
  ↓
确保最终结果是一致的

3. 隔离性(Isolation)

sql 复制代码
分布式锁
  ↓
Commit阶段串行化
  ↓
多个writer不会同时修改表

4. 持久性(Durability)

复制代码
文件系统的可靠性
  ↓
Snapshot一旦写入,永不丢失

相关代码

  • FileStoreCommit:paimon-core/src/main/java/org/apache/paimon/operation/FileStoreCommit.java
  • Snapshot:paimon-api/src/main/java/org/apache/paimon/Snapshot.java
  • CommitMessage:paimon-api/src/main/java/org/apache/paimon/table/sink/CommitMessage.java
相关推荐
ykjhr_3d13 分钟前
数字工具AI智能学伴,助力教育数字化转型
大数据·人工智能·ai·ai人工智能·华锐视点·华锐云空间
Gent_倪18 分钟前
Hadoop生态组件介绍
大数据·hadoop
动恰客流管家18 分钟前
动恰3DV3丨客流统计系统:旺季人手不够淡季闲人太多?客流统计帮你科学优化人力成本
大数据·运维·人工智能·3d
瑞华丽PLM42 分钟前
传统研发协同低效痛点待解,PLM 系统数字化选型助力研发效率提升与转型
大数据·plm·国产plm·瑞华丽plm·瑞华丽
乐迪信息1 小时前
乐迪信息:实时预警,秒级响应:船舶AI异常行为检测算法
大数据·人工智能·算法·安全·目标跟踪
红色星际1 小时前
进军具身机器人和Robotaxi的智驾公司
大数据·人工智能·机器人
Bruce_Liuxiaowei1 小时前
《轻量化制播系统技术应用指南(2026版)》解读:县级融媒体的“减负增效“新路径
大数据·人工智能·媒体
2601_956139421 小时前
文旅行业品牌全案公司哪家强
大数据·人工智能·python
生活观察站1 小时前
中文在线亮相横琴—澳门国际数字艺术博览会国际数字创意论坛:AI漫剧打开内容创作新想象
大数据·人工智能
地球资源数据云1 小时前
1900-2023年中国物种分布点位矢量数据集
大数据·数据结构·数据库·数据仓库·人工智能