一、概述

传统数据湖在数据写入时的事务性支持不够好,随着越来越多的业务关键处理流程移至数据湖,需要一种机制来原子地发布一批数据,即仅保存有效数据,部分失败必须回滚而不会损坏已有数据集。同时查询的结果必须是可重复的,查询端看不到任何部分提取的数据,任何提交的数据都必须可靠地写入。Hudi 提供了强大的 ACID 能力。高效的回滚机制能够保证数据一致性和避免“孤儿文件”或中间状态数据文件残留和产生。

  1. 原子性: 事务整体是一个工作单元,对数据的修改操作,要么全部执行,要么完全不执行,没有第三种状态。
  2. 一致性: 在一个事务执行之前和执行之后数据库都必须处于逻辑上的一致性状态,数据在不同的事务中是相同的。
  3. 隔离性: 并发执行的事务之间是相互隔离的,一个事务内部的状态,对其他事务是不可见的。
  4. 持久性: 当系统发生故障时,持久性确保已提交事务的更新不会丢失,也就是说一旦一个事务提交,保证数据的改变是永久性的。

ACID 四属性的关系可大概表述为:原子性是要求,一致性是目标,隔离性是手段,持久性是结果。如何做到原子性的隔离是实现事务的重中之重。在宏观上,实现事务特性是通过并发控制。在微观上,实现事务要靠隔离

二、设计

Hudi 在 Timeline 上实现了一个文件级、基于日志的并发控制协议,该协议依赖于对云存储的最低限度的原子写入。通过将事件日志构建为进程间协调的核心部分,Hudi 能够提供一些灵活的部署模型,与仅跟踪表快照的纯 OCC 方法相比,这些模型提供更高的并发性。

如果有人在更新表的同时读取表,会发生什么🤔️? 而当多个 writers 同时进行冲突的修改时,又会怎样🤔️?

  1. 通常数据库(比如: MySOL) 通过多版本并发控制 (MVCC) 来解决这个问题,这种方法利用事务日志,所有的更改都会被追加。
  2. 另一种称为优化并发控制 $OCC$ 的方法允许 多个 writers 同时写入,只在最后提交前检查;冲突。如果检测到冲突,其中一个事务会被重试,直到成功。

http://www.taodudu.cc/news/show-3415138.html?action=onclick

Hudi 提供 MVCC 和 OCC 并发控制。

2.1. MVCC

Hudi 的 MVCC 意味着所有的写入必须在其中心日志完全有序。为了提供这种保证,Hudi 将写并发量限制为 1,这意味着在一个给定的时间点上,只能有一个 writer对一个表进行写。

2.2. OCC

数据湖的事务和 multi-writers 正在成为如今构建 Lakehouse 的关键特征,为了摆脱 $MVCC$ 只能有一个 writer 对一个表进行写这种限制,Hudi 提供乐观并发控制(OCC),允许多个 writers 在没有重叠数据文件写入的情况下,并发写入并原子提交到 Hudi 表,保证数据的一致性、完整性和正确性。

2.2.1. 冲突检测

  1. 检查策略

    Hudi 中的每个 commit 都被抽象为 Timeline 上的一个 instance,instance 记录了本次操作的行为、时间戳和状态。冲突检查会在 instant 状态变换的两个节点进行:

    • requested -> inflight 状态

    • inflight -> completed 状态,会进行加锁操作,以实现版本隔离。

    检查策略分为行列两种:

    • 行级别的冲突检查即是不能同时有两个 instant 往同一个 FileGroup 写。

    • 列级别的冲突检查即是可以有两个 instant 往同一个 FileGroup 写,但是两个 instant 写入的 Schema 不可以存在交集。

  2. 检测时机

    • 后期冲突检测

      0.13.0 版本之前,重叠数据文件的冲突检测是在提交元数据之前和数据写入完成之后进行的。如果在最后阶段检测到任何冲突,则可能会浪费计算资源。
      例如现在有两个写作业: job1 会写 10M 的数据到 Hudi 表,包括更新 $FileGroup_1$。另一个 job2 会写 100G 到 Hudi 表,也会更新同一个 $FileGroup_1$。job1 成功完成并提交给 Hudi。几个小时后,job2 写完数据文件 (100G),开始提交元数据。这时候发现和 job1 比较有冲突,job2失败后不得不中业重新运行。显然,大量的计算资源和时间都浪费在了 job2

    • 早期冲突检测
      为了提高资源利用率,Hudi 引入了 $OCC$ 早期冲突检测,利用 Hudi 的标记 marker 机制,在数据写入阶段检测到冲突就提前中止写入。

      Hudi 标记机制可以跟踪作为活动写入事务一部分的所有文件,以及一种可以跟踪表的活动写入者的心跳机制。这可以由其他活动事务/ writer 直接使用来检测其他写入器正在做什么,如果检测到冲突,则尽早中止,从而更快地将集群资源返回给其他作业。

      OCC 中的早期冲突检测在 0.13.0 版本中是实验性的。

      默认情况下,此功能处于关闭状态。用户需要在使用 OCC 进行并发控制时将 hoodie.write.concurrency-early-conflict.detection.enable 设置为 true