【大数据】Hudi 核心知识点详解(二)

news2024/11/26 22:44:06

😊 如果您觉得这篇文章有用 ✔️ 的话,请给博主一个一键三连 🚀🚀🚀 吧 (点赞 🧡、关注 💛、收藏 💚)!!!您的支持 💖💖💖 将激励 🔥 博主输出更多优质内容!!!

  • Hudi 核心知识点详解(一)
  • Hudi 核心知识点详解(二)

Hudi 核心知识点详解(二)

  • 4.Hudi 核心点解析
    • 4.1 基本概念
      • 4.1.1 时间轴 Timeline
      • 4.1.2 文件管理
      • 4.1.3 索引 Index
    • 4.2 表的存储类型
      • 4.2.1 数据计算模型
        • 4.2.1.1 批式模型(Batch)
        • 4.2.1.2 流式模型(Stream)
        • 4.2.1.3 增量模型(Incremental)
      • 4.2.2 查询类型(Query Type)
        • 4.2.2.1 快照查询(Snapshot Queries)
        • 4.2.2.2 增量查询(Incremental Queries)
        • 4.2.2.3 读优化查询(Read Optimized Queries)
      • 4.2.3 Hudi 支持表类型
        • 4.2.3.1 写时复制表(COW)
        • 4.2.3.2 读时合并表(MOR)
        • 4.2.3.3 COW VS MOR
      • 4.2.4 数据写操作类型
        • 4.2.4.1 写流程(UPSERT)
        • 4.2.4.2 写流程(INSERT)

4.Hudi 核心点解析

4.1 基本概念

Hudi 提供了 Hudi 表的概念,这些表支持 CRUD 操作,可以利用现有的大数据集群比如 HDFS 做数据文件存储,然后使用 SparkSQL 或 Hive 等分析引擎进行数据分析查询。

在这里插入图片描述

Hudi 表的三个主要组件:

  • ✅ 有序的时间轴元数据,类似于数据库事务日志。
  • ✅ 分层布局的数据文件:实际写入表中的数据。
  • ✅ 索引(多种实现方式):映射包含指定记录的数据集。

4.1.1 时间轴 Timeline

Hudi 核心:

  • 在所有的表中维护了一个包含在不同的即时(Instant)时间对数据集操作(比如新增、修改或删除)的时间轴(Timeline)。
  • 在每一次对 Hudi 表的数据集操作时都会在该表的 Timeline 上生成一个 Instant,从而可以实现在仅查询某个时间点之后成功提交的数据,或是仅查询某个时间点之前的数据,有效避免了扫描更大时间范围的数据。
  • 可以高效地只查询更改前的文件(如在某个 Instant 提交了更改操作后,仅 query 某个时间点之前的数据,则仍可以 query 修改前的数据)。

在这里插入图片描述
Timeline 是 Hudi 用来管理提交(commit)的抽象,每个 commit 都绑定一个固定时间戳,分散到时间线上。

在 Timeline 上,每个 commit 被抽象为一个 Hoodie Instant,一个 Instant 记录了一次提交(commit)的 行为时间戳、和 状态

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

时间轴(Timeline)的实现类(位于 hudi-common-xx.jar 中),时间轴相关的实现类位于 org.apache.hudi.common.table.timeline 包下。

在这里插入图片描述

4.1.2 文件管理

Hudi 将 DFS 上的数据集组织到基本路径(HoodieWriteConfig.BASEPATHPROP)下的目录结构中。

数据集分为多个分区(DataSourceOptions.PARTITIONPATHFIELDOPT_KEY),这些分区与 Hive 表非常相似,是包含该分区的数据文件的文件夹。

在这里插入图片描述
在每个分区内,文件被组织为文件组,由文件 id 充当唯一标识。每个文件组包含多个文件切片,其中每个切片包含在某个即时时间的提交 / 压缩生成的基本列文件(.parquet)以及一组日志文件(.log),该文件包含自生成基本文件以来对基本文件的插入 / 更新。

在这里插入图片描述
Hudi 的 base file (parquet 文件) 在 footermeta 去记录了 record key 组成的 BloomFilter,用于在 file based index 的实现中实现高效率的 key contains 检测。

Hudi 的 logavro 文件)是自己编码的,通过积攒数据 buffer 以 LogBlock 为单位写出,每个 LogBlock 包含 magic numbersizecontentfooter 等信息,用于数据读、校验和过滤。

在这里插入图片描述

4.1.3 索引 Index

Hudi 通过索引机制提供高效的 Upsert 操作,该机制会将一个 RecordKey + PartitionPath 组合的方式作为唯一标识映射到一个文件 ID,而且这个唯一标识和文件组 / 文件 ID 之间的映射自记录被写入文件组开始就不会再改变。

  • 全局索引:在全表的所有分区范围下强制要求键保持唯一,即确保对给定的键有且只有一个对应的记录。
  • 非全局索引:仅在表的某一个分区内强制要求键保持唯一,它依靠写入器为同一个记录的更删提供一致的分区路径。

在这里插入图片描述

4.2 表的存储类型

4.2.1 数据计算模型

Hudi 是 Uber 主导开发的开源数据湖框架,所以大部分的出发点都来源于 Uber 自身场景,比如司机数据和乘客数据通过订单 Id 来做 Join 等。

在 Hudi 过去的使用场景里,和大部分公司的架构类似,采用批式和流式共存的 Lambda 架构,后来 Uber 提出增量 Incremental 模型,相对批式来讲,更加实时;相对流式而言,更加经济。

在这里插入图片描述

4.2.1.1 批式模型(Batch)

批式模型就是使用 MapReduce、Hive、Spark 等典型的批计算引擎,以小时任务或者天任务的形式来做数据计算。

  • 延迟:小时级延迟或者天级别延迟。这里的延迟不单单指的是定时任务的时间,在数据架构里,这里的延迟时间通常是定时任务间隔时间 + 一系列依赖任务的计算时间 + 数据平台最终可以展示结果的时间。数据量大、逻辑复杂的情况下,小时任务计算的数据通常真正延迟的时间是 2 − 3 2-3 23 小时。
  • 数据完整度:数据较完整。以处理时间为例,小时级别的任务,通常计算的原始数据已经包含了小时内的所有数据,所以得到的数据相对较完整。但如果业务需求是事件时间,这里涉及到终端的一些延迟上报机制,在这里,批式计算任务就很难派上用场。
  • 成本:成本很低。只有在做任务计算时,才会占用资源,如果不做任务计算,可以将这部分批式计算资源出让给在线业务使用。从另一个角度来说成本是挺高的,如原始数据做了一些增删改查,数据晚到的情况,那么批式任务是要全量重新计算。

在这里插入图片描述

4.2.1.2 流式模型(Stream)

流式模型,典型的就是使用 Flink 来进行实时的数据计算。

  • 延迟:很短,甚至是实时。
  • 数据完整度:较差。因为流式引擎不会等到所有数据到齐之后再开始计算,所以有一个 watermark 的概念,当数据的时间小于 watermark 时,就会被丢弃,这样是无法对数据完整度有一个绝对的保障。在互联网场景中,流式模型主要用于活动时的数据大盘展示,对数据的完整度要求并不算很高。在大部分场景中,用户需要开发两个程序,一是流式数据生产流式结果,二是批式计算任务,用于次日修复实时结果。
  • 成本:很高。因为流式任务是常驻的,并且对于多流 Join 的场景,通常要借助内存或者数据库来做 state 的存储,不管是序列化开销,还是和外部组件交互产生的额外 IO,在大数据量下都是不容忽视的。

在这里插入图片描述

4.2.1.3 增量模型(Incremental)

针对批式和流式的优缺点,Uber 提出了 增量模型Incremental Mode),相对批式来讲,更加实时;相对流式而言,更加经济。

增量模型,简单来讲,是以 mini batch 的形式来跑准实时任务。Hudi 在增量模型中支持了两个最重要的特性:

  • Upsert:这个主要是解决批式模型中,数据不能插入、更新的问题,有了这个特性,可以往 Hive 中写入增量数据,而不是每次进行完全的覆盖。(Hudi 自身维护了 keyfile 的映射,所以当 upsert 时很容易找到 key 对应的文件)
  • Incremental Query:增量查询,减少计算的原始数据量。以 Uber 中司机和乘客的数据流 Join 为例,每次抓取两条数据流中的增量数据进行批式的 Join 即可,相比流式数据而言,成本要降低几个数量级。

在这里插入图片描述

4.2.2 查询类型(Query Type)

Hudi 支持三种不同的查询表的方式:Snapshot QueriesIncremental QueriesRead Optimized Queries

在这里插入图片描述

4.2.2.1 快照查询(Snapshot Queries)
  • 查询某个增量提交操作中数据集的最新快照,先进行动态合并最新的基本文件(Parquet)和增量文件(Avro)来提供近实时数据集(通常会存在几分钟的延迟)。
  • 读取所有 partition 下每个 FileGroup 最新的 FileSlice 中的文件,Copy On Write 表读 parquet 文件,Merge On Read 表读 parquet + log 文件。

在这里插入图片描述

4.2.2.2 增量查询(Incremental Queries)
  • 仅查询新写入数据集的文件,需要指定一个 Commit / Compaction 的即时时间(位于 Timeline 上的某个 Instant)作为条件,来查询此条件之后的新数据。
  • 可查看自给定 commit / delta commit 即时操作以来新写入的数据,有效的提供变更流来启用增量数据管道。

在这里插入图片描述

4.2.2.3 读优化查询(Read Optimized Queries)
  • 直接查询基本文件(数据集的最新快照),其实就是列式文件(Parquet)。并保证与非 Hudi 列式数据集相比,具有相同的列式查询性能。
  • 可查看给定的 commit / compact 即时操作的表的最新快照。
  • 读优化查询和快照查询相同仅访问基本文件,提供给定文件片自上次执行压缩操作以来的数据。通常查询数据的最新程度的保证取决于压缩策略。

在这里插入图片描述

4.2.3 Hudi 支持表类型

Hudi 提供两类型表:写时复制Copy on WriteCOW)表和 读时合并Merge On ReadMOR)表。

  • 对于 Copy-On-Write Table,用户的 update 会重写数据所在的文件,所以是一个写放大很高,但是读放大为 0 0 0,适合 写少读多 的场景。
  • 对于 Merge-On-Read Table,整体的结构有点像 LSM-Tree,用户的写入先写入到 delta data 中,这部分数据使用行存,这部分 delta data 可以手动 merge 到存量文件中,整理为 parquet 的列存结构。

在这里插入图片描述

4.2.3.1 写时复制表(COW)

Copy on Write 简称 COW,顾名思义,它是在数据写入的时候,复制一份原来的拷贝,在其基础上添加新数据。

正在读数据的请求,读取的是最近的完整副本,这类似 MySQL 的 MVCC 的思想。

在这里插入图片描述

  • ✅ 优点:读取时,只读取对应分区的一个数据文件即可,较为高效。
  • ⭕ 缺点:数据写入的时候,需要复制一个先前的副本再在其基础上生成新的数据文件,这个过程比较耗时。

在这里插入图片描述
COW 表主要使用列式文件格式(Parquet)存储数据,在写入数据过程中,执行同步合并,更新数据版本并重写数据文件,类似 RDBMS 中的 B-Tree 更新。

  • 更新 update:在更新记录时,Hudi 会先找到包含更新数据的文件,然后再使用更新值(最新的数据)重写该文件,包含其他记录的文件保持不变。当突然有大量写操作时会导致重写大量文件,从而导致极大的 I/O 开销。
  • 读取 read:在读取数据时,通过读取最新的数据文件来获取最新的更新,此存储类型适用于少量写入和大量读取的场景。
4.2.3.2 读时合并表(MOR)

Merge On Read 简称 MOR,新插入的数据存储在 delta log 中,定期再将 delta log 合并进行 parquet 数据文件。

读取数据时,会将 delta log 跟老的数据文件做 merge,得到完整的数据返回。下图演示了 MOR 的两种数据读写方式。

在这里插入图片描述

  • ✅ 优点:由于写入数据先写 delta log,且 delta log 较小,所以写入成本较低。
  • ⭕ 缺点:需要定期合并整理 compact,否则碎片文件较多。读取性能较差,因为需要将 delta log 和老数据文件合并。

MOR 表是 COW 表的升级版,它使用列式(parquet)与行式(avro)文件混合的方式存储数据。在更新记录时,类似 NoSQL 中的 LSM-Tree 更新。

  • 更新:在更新记录时,仅更新到增量文件(Avro)中,然后进行异步(或同步)的 compaction,最后创建列式文件(parquet)的新版本。此存储类型适合频繁写的工作负载,因为新记录是以追加的模式写入增量文件中。
  • 读取:在读取数据集时,需要先将增量文件与旧文件进行合并,然后生成列式文件成功后,再进行查询。
4.2.3.3 COW VS MOR

对于写时复制(COW)和读时合并(MOR)writer 来说,Hudi 的 WriteClient 是相同的。

  • 🚀 COW 表,用户在 snapshot 读取的时候会扫描所有最新的 FileSlice 下的 base file
  • 🚀 MOR 表,在 READ OPTIMIZED 模式下,只会读最近的经过 compactioncommit
权衡写时复制 COW读时合并 MOR
数据延迟更高更低
更新代价(I/O)更高(重写整个 parquet 文件)更低(追加到增量日志)
Parquet 文件大小更小(高更新代价(I/O))更大(低更新代价)
写放大更高更低(取决于压缩策略)
适用场景写少读多写多读少

4.2.4 数据写操作类型

在 Hudi 数据湖框架中支持三种方式写入数据:UPSERT插入更新)、INSERT插入)和 BULK INSERT写排序)。

  • UPSERT:默认行为,数据先通过 index 打标(INSERT / UPDATE),有一些启发式算法决定消息的组织以优化文件的大小。

  • INSERT:跳过 index,写入效率更高

  • BULK INSERT:写排序,对大数据量的 Hudi 表初始化友好,对文件大小的限制 best effort(写 HFile)。

在这里插入图片描述

4.2.4.1 写流程(UPSERT)

1️⃣ Copy On Write 类型表,UPSERT 写入流程

  • 第一步:先对 records 按照 record key 去重。
  • 第二步:首先对这批数据创建索引 (HoodieKeyHoodieRecordLocation);通过索引区分哪些 recordsupdate,哪些 recordsinsertkey 第一次写入)。
  • 第三步:对于 update 消息,会直接找到对应 key 所在的最新 FileSlice 的 base 文件,并做 merge 后写新的 base file (新的 FileSlice)。
  • 第四步:对于 insert 消息,会扫描当前 partition 的所有 SmallFile(小于一定大小的 base file),然后 merge 写新的 FileSlice;如果没有 SmallFile,直接写新的 FileGroup + FileSlice

2️⃣ Merge On Read 类型表,UPSERT 写入流程

  • 第一步:先对 records 按照 record key 去重(可选)。
  • 第二步:首先对这批数据创建索引 (HoodieKeyHoodieRecordLocation);通过索引区分哪些 recordsupdate,哪些 recordsinsertkey 第一次写入)。
  • 第三步:如果是 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 slicelog file 大小达到阈值会 roll over 一个新的。
4.2.4.2 写流程(INSERT)

1️⃣ Copy On Write 类型表,INSERT 写入流程

  • 第一步:先对 records 按照 record key 去重(可选);
  • 第二步:不会创建 Index
  • 第三步:如果有小的 base file 文件,merge base file,生成新的 FileSlice + base file,否则直接写新的 FileSlice + base file

2️⃣ Merge On Read 类型表,INSERT 写入流程

  • 第一步:先对 records 按照 record key 去重(可选);
  • 第二步:不会创建 Index
  • 第三步:如果 log file 可索引,并且有小的 FileSlice,尝试追加或写最新的 log file;如果 log file 不可索引,写一个新的 FileSlice + base file

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1300923.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

设计模式篇---代理模式

文章目录 概念结构实例静态代理动态代理 总结 概念 代理模式:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。 比如我们想从其他国家买东西,但我们无法直接联系外国的商家,可以找代理商,让他们…

ipa文件怎么去除包体内的插件在线签名工具步骤

当开发者完成iOS应用的开发并构建完成后,应用程序会被打包为一个.ipa文件,这是一个iOS App Store的安装包格式。在某些情况下,开发者可能需要去除.ipa文件中包含的插件(通常指的是app extension、frameworks或watch apps等&#x…

内测分发是什么?十年的前端开发者带你了解

内测分发是软件开发过程中的一个阶段,特别指软件还未完全完成或准备对外广泛发布前,向一定范围的用户群体提供该软件版本的测试机会,以便收集反馈和修复潜在的问题。在讲解内测分发之前,我们需要明确几个相关概念: 软件…

12.视图

目录 1.视图的含义与作用 2.视图的创建与查看 1.创建视图的语法形式 2、查看视图: 1.使用DESCRIBE语句查看视图基本信息 2.使用SHOW TABLE STATUS语查看视图基本信息查看视图的信息 3.使用SHOW CREATE VIEW语查看视图详细信息 4.在views表中查看视图详细信息…

23.12.10日总结

周总结 这周三的晚自习,学姐讲了一下git的合作开发,还有懒加载,防抖,节流 答辩的时候问了几个问题: 为什么在js中0.10.2!0.3? 在js中进行属性运算时,会出现0.10.20.300000000000000004js遵循IEEE754标…

DIP——边缘提取与分割

1.使用canny算法进行边缘提取 本实验比较简单,基本思路是对原图像进行一个高斯模糊处理,用于去噪,之后转换为灰度图,直接调用cv库中的canny记性边缘提取。若想直接得到彩色边缘,则通过按位与操作,将原始彩色…

docker-centos中基于keepalived+niginx模拟主从热备完整过程

文章目录 一、环境准备二、主机1、环境搭建1.1 镜像拉取1.2 创建网桥1.3 启动容器1.4 配置镜像源1.5 下载工具包1.6 下载keepalived1.7 下载nginx 2、配置2.1 配置keepalived2.2 配置nginx2.2.1 查看nginx.conf2.2.2 修改index.html 3、启动3.1 启动nginx3.2 启动keepalived 4、…

MySQL - 聚簇索引和非聚簇索引,回表查询,索引覆盖,索引下推,最左匹配原则

聚簇索引和非聚簇索引 聚簇索引和非聚簇索引是 InnoDB 里面的叫法 一张表它一定有聚簇索引,一张表只有一个聚簇索引在物理上也是连续存储的 它产生的过程如下: 表中有无有主键索引,如果有,则使用主键索引作为聚簇索引;…

Kafka 最佳实践:构建可靠、高性能的分布式消息系统

Apache Kafka 是一个强大的分布式消息系统,被广泛应用于实时数据流处理和事件驱动架构。为了充分发挥 Kafka 的优势,需要遵循一些最佳实践,确保系统在高负载下稳定运行,数据可靠传递。本文将深入探讨 Kafka 的一些最佳实践&#x…

TailwindCSS 配置可视化检查器

问题 TailwindCSS 框架为我们提供了大量默认的类和属性,而且开发者也能够自定义类和配置。 对于初学者来说,这些配置其实是比较复杂的,这也是tailwindcss最大的入手成本,开发者的记忆负担和心智负担也都比较大。 有没有办法能够…

【BUG】微信小程序image不会随着url动态变化

问题描述: 第一次打开界面,显示的是默认头像而不是用户头像,似乎image里面的src只要第一次有值就不会再更新了 解决 不要给src里面的变量设置初始值,而是直接赋空值

ChatGPT 应用开发(一)ChatGPT OpenAI API 免代理调用方式(通过 Cloudflare 的 AI Gateway)

前言 开发 ChatGPT 应用,我觉得最前置的点就是能使用 ChatGPT API 接口。首先我自己要能成功访问,这没问题,会魔法就可以本地调用。 那用户如何调用到我的应用 API 呢,我的理解是通过用户能访问到的中转服务器向 OpenAI 发起访问…

[软件工具]文本去重含有重复的全部删除不是保留一个重复的方法

文本去重含有重复的全部删除不是保留一个重复的方法 第一步:首先打开软件 第二步:设置好保存目录后,将文件夹拖拽到列表,软件会自动识别导入txt 第三步:点击开始处理,即可完成任务 本软件支持批量处理&a…

Go1.21.0 程序启动过程

版本说明 Go 1.21.0操作系统:Windows11 Intel64 结论先行 开发关注版 在 Go 语言中,启动顺序通常如下: 导入包:首先,Go 编译器按照源文件中的 import 语句导入所有需要的包。初始化常量和变量:接着&am…

uc_16_UDP协议_HTTP协议

1 UDP协议 适合游戏、视频等情景,安全性要求不高,效率要求高。 1)UDP不提供客户机与服务器的链接: UDP的客户机与服务器不必存在长期关系。一个UDP的客户机在通过一个套接字向一个UDP服务器发送了一个数据报之后,马上…

UE小:物品拼装功能

蓝图B1的实现步骤: 获取玩家控制器和视角:首先获取玩家控制器,然后使用Deproject Screen to World节点将屏幕上的鼠标位置转换为世界空间中的一条射线。 射线检测:使用Line Trace by Channel或Line Trace for Objects节点发射射线…

【数学建模】《实战数学建模:例题与讲解》第十讲-时间序列预测(含Matlab代码)

【数学建模】《实战数学建模:例题与讲解》第十讲-时间序列预测(含Matlab代码) 基本概念移动平均(Moving Average, MA):指数平滑法(Exponential Smoothing):季节性调整(Seasonal Adju…

并发编程的基本概念

进程与线程 进程 程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至 CPU,数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理 IO 的当一个程序被运行&…

【电路笔记】-压敏电阻

压敏电阻 文章目录 压敏电阻1、概述2、交流波形瞬变3、抗静电能力4、特性曲线5、压敏电阻电容值6、金属氧化物压敏电阻7、压敏电阻应用8、总结 压敏电阻是一种无源两端固态半导体器件,用于为电气和电子电路提供保护。 1、概述 与提供过电流保护的保险丝或断路器不同…

linux 14网站架构 编译安装mysql数据库

目录 LNMP网站架构下载源码包mysql 下载位置 mysql 安装1.1、清理安装环境:1.2、创建mysql用户1.3、从官网下载tar包1.4、安装编译工具1.5、解压1.6、编译安装编译安装三部曲1.7、初始化初始化,只需要初始化一次1.8、启动mysql1.9、登录mysql1.10、systemctl启动方式…