1. 场景

https://hudi.apache.org/docs/use_cases.html

  • 近实时写入

    • 减少碎片化工具的使用
    • CDC 增量导入 RDBMS 数据
    • 限制小文件的大小和数量
  • 近实时分析
    • 相对于秒级存储 (Druid, OpenTSDB) ,节省资源
    • 提供分钟级别时效性,支撑更高效的查询
    • Hudi 作为 lib,非常轻量
  • 增量 pipeline
    • 区分 arrivetime 和 event time 处理延迟数据
    • 更短的调度 interval 减少端到端延迟 (小时 -> 分钟) => Incremental Processing
  • 增量导出
    • 替代部分 Kafka 的场景,数据导出到在线服务存储 e.g. ES

2. 概念/术语

https://hudi.apache.org/docs/concepts.html

2.1 Timeline

Timeline 是 HUDI 用来管理提交(commit)的抽象,每个 commit 都绑定一个固定时间戳,分散到时间线上。在 Timeline 上,每个 commit 被抽象为一个 HoodieInstant,一个 instant 记录了一次提交 (commit) 的行为、时间戳、和状态。

HUDI 的读写 API 通过 Timeline 的接口可以方便的在 commits 上进行条件筛选,对 history 和 on-going 的 commits 应用各种策略,快速筛选出需要操作的目标 commit。

2.2 Time

Arrival time: 数据到达 Hudi 的时间,commit time

Event time: record 中记录的时间

上图中采用时间(小时)作为分区字段,从 10:00 开始陆续产生各种 commits,10:20 来了一条 9:00 的数据,该数据仍然可以落到 9:00 对应的分区,通过 timeline 直接消费 10:00 之后的增量更新(只消费有新 commits 的 group),那么这条延迟的数据仍然可以被消费到。

2.3 文件管理

2.3.1 文件版本

一个新的 base commit time 对应一个新的 FileSlice,实际就是一个新的数据版本。HUDI 通过 TableFileSystemView 抽象来管理 table 对应的文件,比如找到所有最新版本 FileSlice 中的 base file (Copy On Write Snapshot 读)或者 base + log files(Merge On Read 读)。

通过 Timeline 和 TableFileSystemView 抽象,HUDI 实现了非常便捷和高效的表文件查找。

2.3.3 文件格式

Hoodie 的每个 FileSlice 中包含一个 base file (merge on read 模式可能没有)和多个 log file (copy on write 模式没有)。

每个文件的文件名都带有其归属的 FileID(即 FileGroup Identifier)和 base commit time(即 InstanceTime)。通过文件名的 group id 组织 FileGroup 的 logical 关系;通过文件名的 base commit time 组织 FileSlice 的逻辑关系。

HUDI 的 base file (parquet 文件) 在 footer 的 meta 去记录了 record key 组成的 BloomFilter,用于在 file based index 的实现中实现高效率的 key contains 检测。只有不在 BloomFilter 的 key 才需要扫描整个文件消灭假阳。

HUDI 的 log (avro 文件)是自己编码的,通过积攒数据 buffer 以 LogBlock 为单位写出,每个 LogBlock 包含 magic number、size、content、footer 等信息,用于数据读、校验和过滤。

2.4 Index

Hoodie key (record key + partition path) 和 file id (FileGroup) 之间的映射关系,数据第一次写入文件后保持不变,所以,一个 FileGroup 包含了一批 record 的所有版本记录。Index 用于区分消息是 INSERT 还是 UPDATE。

2.4.1 Index的创建过程

1. BloomFilter Index
  • 新增 records 找到映射关系:record key => target partition
  • 当前最新的数据 找到映射关系:partition => (fileID, minRecordKey, maxRecordKey) LIST (如果是 base files 可加速)
  • 新增 records 找到需要搜索的映射关系:fileID => HoodieKey(record key + partition path) LIST,key 是候选的 fileID
  • 通过 HoodieKeyLookupHandle 查找目标文件(通过 BloomFilter 加速)
2. Flink State-based Index

HUDI 在 0.8.0 版本中实现的 Flink witer,采用了 Flink 的 state 作为底层的 index 存储,每个 records 在写入之前都会先计算目标 bucket ID,不同于 BloomFilter Index,避免了每次重复的文件 index 查找。

2.5 Table 类型

2.5.1 Copy On Write

Copy On Write 类型表每次写入都会生成一个新的持有 base file(对应写入的 instant time ) 的 FileSlice。

用户在 snapshot 读取的时候会扫描所有最新的 FileSlice 下的 base file。

2.5.2 Merge On Read

Merge On Read 表的写入行为,依据 index 的不同会有细微的差别:

  • 对于 BloomFilter 这种无法对 log file 生成 index 的索引方案,对于 INSERT 消息仍然会写 base file (parquet format),只有 UPDATE 消息会 append log 文件(因为 base file 总已经记录了该 UPDATE 消息的 FileGroup ID)。
  • 对于可以对 log file 生成 index 的索引方案,例如 Flink writer 中基于 state 的索引,每次写入都是 log format,并且会不断追加和 roll over。

Merge On Read 表的读在 READ OPTIMIZED 模式下,只会读最近的经过 compaction 的 commit。

3. 数据写

3.1 写操作

  • UPSERT:默认行为,数据先通过 index 打标(INSERT/UPDATE),有一些启发式算法决定消息的组织以优化文件的大小 => CDC 导入
  • INSERT:跳过 index,写入效率更高 => Log Deduplication
  • BULK_INSERT:写排序,对大数据量的 Hudi 表初始化友好,对文件大小的限制 best effort(写 HFile)

3.1.1 写流程(UPSERT)

1. Copy On Write
  • 先对 records 按照 record key 去重
  • 首先对这批数据创建索引 (HoodieKey => HoodieRecordLocation);通过索引区分哪些 records 是 update,哪些 records 是 insert(key 第一次写入)
  • 对于 update 消息,会直接找到对应 key 所在的最新 FileSlice 的 base 文件,并做 merge 后写新的 base file (新的 FileSlice)
  • 对于 insert 消息,会扫描当前 partition 的所有 SmallFile(小于一定大小的 base file),然后 merge 写新的 FileSlice;如果没有 SmallFile,直接写新的 FileGroup + FileSlice
2. Merge On Read
  • 先对 records 按照 record key 去重(可选)
  • 首先对这批数据创建索引 (HoodieKey => HoodieRecordLocation);通过索引区分哪些 records 是 update,哪些 records 是 insert(key 第一次写入)
  • 如果是 insert 消息,如果 log file 不可建索引(默认),会尝试 merge 分区内最小的 base file (不包含 log file 的 FileSlice),生成新的 FileSlice;如果没有 base file 就新写一个 FileGroup + FileSlice + base file;如果 log file 可建索引,尝试 append 小的 log file,如果没有就新写一个 FileGroup + FileSlice + base file
  • 如果是 update 消息,写对应的 file group + file slice,直接 append 最新的 log file(如果碰巧是当前最小的小文件,会 merge base file,生成新的 file slice)log file 大小达到阈值会 roll over 一个新的

3.1.2 写流程(INSERT)

1. Copy On Write
  • 先对 records 按照 record key 去重(可选)
  • 不会创建 Index
  • 如果有小的 base file 文件,merge base file,生成新的 FileSlice + base file,否则直接写新的 FileSlice + base file
2. Merge On Read
  • 先对 records 按照 record key 去重(可选)
  • 不会创建 Index
  • 如果 log file 可索引,并且有小的 FileSlice,尝试追加或写最新的 log file;如果 log file 不可索引,写一个新的 FileSlice + base file

3.1.3 工具

  • DeltaStreamer
  • Datasource Writer
  • Flink SQL API

3.1.4 Key 生成策略

用来生成 HoodieKey(record key + partition path),目前支持以下策略:

  • 支持多个字段组合 record keys
  • 支持多个字段组合的 parition path (可定制时间格式,Hive style path name)
  • 非分区表

3.1.5 删除策略

  • 逻辑删:将 value 字段全部标记为 null
  • 物理删:
    • 通过 OPERATION_OPT_KEY 删除所有的输入记录
    • 配置 PAYLOAD_CLASS_OPT_KEY = org.apache.hudi.EmptyHoodieRecordPayload 删除所有的输入记录
    • 在输入记录添加字段:_hoodie_is_deleted

4. 数据读

4.1 Snapshot 读

读取所有 partiiton 下每个 FileGroup 最新的 FileSlice 中的文件,Copy On Write 表读 parquet 文件,Merge On Read 表读 parquet + log 文件

4.2 Incremantal 读

https://hudi.apache.org/docs/querying_data.html#spark-incr-query,当前的 Spark data source 可以指定消费的起始和结束 commit 时间,读取 commit 增量的数据集。但是内部的实现不够高效:拉取每个 commit 的全部目标文件再按照系统字段 hoodie_commit_time apply 过滤条件。

4.3 Streaming 读

0.8.0 版本的 HUDI Flink writer 支持实时的增量订阅,可用于同步 CDC 数据,日常的数据同步 ETL pipeline。Flink 的 streaming 读做到了真正的流式读取,source 定期监控新增的改动文件,将读取任务下派给读 task。

5. Compaction

  • 没有 base file:走 copy on write insert 流程,直接 merge 所有的 log file 并写 base file
  • 有 base file:走 copy on write upsert 流程,* 先读 log file 建 index,再读 base file,最后读 log file 写新的 base file

Flink 和 Spark streaming 的 writer 都可以 apply 异步的 compaction 策略,按照间隔 commits 数或者时间来触发 compaction 任务,在独立的 pipeline 中执行。

6. 总结

通过对写流程的梳理我们了解到 HUDI 相对于其他数据湖方案的核心优势:

  • 写入过程充分优化了文件存储的小文件问题,Copy On Write 写会一直将一个 bucket (FileGroup)的 base 文件写到设定的阈值大小才会划分新的 bucket;Merge On Read 写在同一个 bucket 中,log file 也是一直 append 直到大小超过设定的阈值 roll over。
  • 对 UPDATE 和 DELETE 的支持非常高效,一条 record 的整个生命周期操作都发生在同一个 bucket,不仅减少小文件数量,也提升了数据读取的效率(不必要的 join 和 merge)。

0.8.0 的 HUDI Flink 支持了 streaming 消费 HUDI 表,在后续版本还会支持 watermark 机制,让 HUDI Flink 承担 streaming ETL pipeline 的中间层,成为数据湖/仓建设中流批一体的中间计算层。

Apache Hudi核心概念一网打尽的更多相关文章

  1. Apache NiFi 核心概念和关键特性

    本文来源于官方文档翻译 NiFi 的核心概念 NiFi 最早是美国国家安全局内部使用的工具,用来投递海量的传感器数据.后来由 apache 基金会开源.天生就具备强大的基因.NiFi基本设计理念与 F ...

  2. Apache Shiro 核心概念

    转自:http://blog.csdn.net/peterwanghao/article/details/8015571 Shiro框架中有三个核心概念:Subject ,SecurityManage ...

  3. Apache Storm 核心概念

    前言: Storm读取实时数据流,并传递给处理单元,最终输出处理后的数据. 下图描述了storm的处理数据的主要结构. 元组(Tuple) :       元组是Storm提供的一个轻量级的数据格式, ...

  4. 使用Apache Spark和Apache Hudi构建分析数据湖

    1. 引入 大多数现代数据湖都是基于某种分布式文件系统(DFS),如HDFS或基于云的存储,如AWS S3构建的.遵循的基本原则之一是文件的"一次写入多次读取"访问模型.这对于处理 ...

  5. Hudi基本概念

    Apache Hudi(发音为"Hoodie")在DFS的数据集上提供以下流原语 插入更新 (如何改变数据集?) 增量拉取 (如何获取变更的数据?) 在本节中,我们将讨论重要的概念 ...

  6. Nifi简介及核心概念整理

    简介 Apache NiFi 是一个易于使用.功能强大而且可靠的数据拉取.数据处理和分发系统,用于自动化管理系统间的数据流. 它支持高度可配置的指示图的数据路由.转换和系统中介逻辑,支持从多种数据源动 ...

  7. Apache Hudi 设计与架构最强解读

    感谢 Apache Hudi contributor:王祥虎 翻译&供稿. 欢迎关注微信公众号:ApacheHudi 本文将介绍Apache Hudi的基本概念.设计以及总体基础架构. 1.简 ...

  8. Uber基于Apache Hudi构建PB级数据湖实践

    1. 引言 从确保准确预计到达时间到预测最佳交通路线,在Uber平台上提供安全.无缝的运输和交付体验需要可靠.高性能的大规模数据存储和分析.2016年,Uber开发了增量处理框架Apache Hudi ...

  9. Maven的几个核心概念

    POM (Project Object Model) 一个项目所有的配置都放置在 POM 文件中:定义项目的类型.名字,管理依赖关系,定制插件的行为等等.比如说,你可以配置 compiler 插件让它 ...

  10. Maven介绍,包括作用、核心概念、用法、常用命令、扩展及配置

    由浅入深,主要介绍maven的用途.核心概念(Pom.Repositories.Artifact.Build Lifecycle.Goal).用法(Archetype意义及创建各种项目).maven常 ...

随机推荐

  1. rails: 的cookie小结

    cookie会随着浏览器每次发起的请求(request)传给服务器进行读取,而服务器则会在应答(response)中携带cookie写在本机上.因此,cookie是存储在本地的.而且由于cookie的 ...

  2. C++ 文件读取

    #include <iostream> #include <fstream> #include <string> using namespace std; int ...

  3. RVA与Offset的换算函数

    function RVAToFileOffset(FileName:string; RVA: Cardinal): Cardinal; var   MemPE: TFileStream;   PEDo ...

  4. 12月上旬poj其他题

    poj3170 1,4两遍bfs: poj3171 改一改poj2376即可 poj3172 dfs+剪枝 其实增长速度很快,n<=40,题目吓你的: poj3661 比较经典的dp:设f[i, ...

  5. java——多态

    多态定义:某一类事物的多种存在形态.对象的多态性.猫这类事物即具备猫的形态,又具备着动物的形态,这就是对象的多态性.简单说:就是一个对象对应着不同类型.多态在代码中的体现:父类或者接口的引用指向其子类 ...

  6. 测试框架Unitest的运行原理,以及多个测试类中的执行顺序以及简化方法

    单元测试单元测试(unit testing)是指对软件中的最小可测试单元进行检查和验证.对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一 ...

  7. 用户未登录或Session超时时重定向到登录页,不那么简单

    在网站开发中,我们经常有这样的场景出现: 情景1:对未登录的用户或没有权限的用户,当其想访问某个受限网页时,系统要能够自动转到登录页面.   情景2:对于用session保存用户状态的情况还有这样一种 ...

  8. [转] 由Request Method:OPTIONS初窥CORS

    刚接触前端的时候,以为HTTP的Request Method只有GET与POST两种,后来才了解到,原来还有HEAD.PUT.DELETE.OPTIONS…… 目前的工作中,HEAD.PUT.DELE ...

  9. c 各种编译器(gcc clang)

    很多时候,出现一些类似GNU,GCC,CLANG,LLVM等与编译器有关的名词的时候,都不太清楚它到底是干嘛的,理解这些东西后, 对于xcode中很多配置型的需求修改起来都会得心应手,因此有必要了解透 ...

  10. 如何让两个div并排,并且div要看得见边框

    <div style="float:left; width:100px; height:100px; border:2px solid #0000FF;"></d ...