Apache Hudi简介、与Kudu、Hive、 HBase对比

Apache Hudi是一种大数据存储框架,专注于实时数据更新和查询。它提供了两种流原语:插入更新和增量拉取,支持数据的实时更新而非处理的实时。Hudi有两种表存储类型:COW(写时复制)和MOR(读时合并),分别适用于不同的读写场景。Hudi通过索引优化读取性能,支持Spark、Hive和Presto等工具进行查询,并具备数据修复和GDPR合规性。与Kudu、Hive和HBase等相比,Hudi在实时分析和数据湖集成方面具有优势。

一、Apache Hudi

数据实时处理和实时的数据

        实时分为处理的实时和数据的实时,即席分析是要求对数据实时的处理,马上要得到对应的结果,Flink、Spark Streaming是用来对实时数据的实时处理,数据要求实时,处理也要迅速,数据不实时,处理也不及时的场景则是我们的数仓T+1数据

而本文探讨的Apache Hudi,对应的场景是数据的实时,而非处理的实时。它旨在将Mysql中的时候以近实时的方式映射到大数据平台,比如Hive中。

痛点和业务场景和技术选型

数据调度:T+1,慢,实时化
数据同步:T+1,merge,数据量大,周期长
修复回刷:修复好后数据回刷,数据量大

传统的离线数仓,通常数据是T+1的,不能满足对当日数据分析的需求,而流式计算一般是基于窗口,并且窗口逻辑相对比较固定。
现有一类特殊的需求,业务分析比较熟悉现有事务数据库的数据结构,并且希望有很多即席分析,这些分析包含当日比较实时的数据。惯常他们是基于Mysql从库,直接通过Sql做相应的分析计算。但很多时候会遇到如下障碍

  • 数据量较大、分析逻辑较为复杂时,Mysql从库耗时较长
  • 一些跨库的分析无法实现

        因此,一些弥合在OLTP和OLAP之间的技术框架出现,典型有TiDB。它能同时支持OLTP和OLAP。而诸如Apache Hudi和Apache Kudu则相当于现有OLTP和OLAP技术的桥梁。他们能够以现有OLTP中的数据结构存储数据,支持CRUD,同时提供跟现有OLAP框架的整合(如Hive,Impala),以实现OLAP分析

首先来看Hudi的架构体系,通过Spark/Flink将上游数据同步到数据湖的Raw Tables中,并可对Raw Tables进行增删改查,与内部需求及痛点匹配度非常高。

Apache Hudi

Apache Hudi 在基于 HDFS 数据存储之上,提供了两种流原语:

  1. 插入更新
  2. 增量拉取

        一般来说,我们会将大量数据存储到HDFS,新数据增量写入,而旧数据鲜有改动,特别是在经过数据清洗,放入数据仓库的场景。而且在数据仓库如 hive中,对于update的支持非常有限,计算昂贵。另一方面,若是有仅对某段时间内新增数据进行分析的场景,则hive、presto、hbase等也未提供原生方式,而是需要根据时间戳进行过滤分析。

        在此需求下,Hudi可以提供这两种需求的实现。第一个是对record级别的更新,另一个是仅对增量数据的查询。且Hudi提供了对Hive、presto、Spark的支持,可以直接使用这些组件对Hudi管理的数据进行查询。

这两种原语分别是:

  • Update/Delete记录:Hudi使用细粒度的文件/记录级别索引来支持Update/Delete记录,同时还提供写操作的事务保证。查询会处理最后一个提交的快照,并基于此输出结果。
  • 变更流:Hudi对获取数据变更提供了一流的支持:可以从给定的时间点获取给定表中已updated/inserted/deleted的所有记录的增量流,并解锁新的查询姿势(类别)。

使用Hudi的优点

  • 使用Bloomfilter机制+二次查找,可快速确定记录是更新还是新增
  • 更新范围小,是文件级别,不是表级别
  • 文件大小与hdfs的Blocksize保持一致
  • 数据文件使用parquet格式,充分利用列存的优势(dremal论文实现)
  • 提供了可扩展的大数据更新框架
  • 并发度由spark控制

设计原则

  • 流式读/写:Hudi借鉴了数据库设计的原理,从零设计,应用于大型数据集记录流的输入和输出。为此,Hudi提供了索引实现,可以将记录的键快速映射到其所在的文件位置。同样,对于流式输出数据,Hudi通过其特殊列添加并跟踪记录级的元数据,从而可以提供所有发生变更的精确增量流。
  • 自管理:Hudi注意到用户可能对数据新鲜度(写友好)与查询性能(读/查询友好)有不同的期望,它支持了三种查询类型,这些类型提供实时快照,增量流以及稍早的纯列数据。在每一步,Hudi都努力做到自我管理(例如自动优化编写程序的并行性,保持文件大小)和自我修复(例如:自动回滚失败的提交),即使这样做会稍微增加运行时成本(例如:在内存中缓存输入数据已分析工作负载)。如果没有这些内置的操作杠杆/自我管理功能,这些大型流水线的运营成本通常会翻倍。
  • 万物皆日志:Hudi还具有 append only、云数据友好的设计,该设计实现了日志结构化存储系统的原理,可以无缝管理所有云提供商的数据。
  • 键-值数据模型:在写方面,Hudi表被建模为键值对数据集,其中每条记录都有一个唯一的记录键。此外,一个记录键还可以包括分区路径,在该路径下,可以对记录进行分区和存储。这通常有助于减少索引查询的搜索空间。

Hudi提供了以下功能来对基础数据进行写入、查询,这使其成为大型数据湖的重要模块:

  • 支持快速,可插拔索引的upsert();
  • 高效、只扫描新数据的增量查询;
  • 原子性的数据发布和回滚,支持恢复的Savepoint;
  • 使用mvcc(多版本并发控制)风格设计的读和写快照隔离;
  • 使用统计信息管理文件大小;
  • 已有记录update/delta的自管理压缩;
  • 审核数据修改的时间轴元数据;
  • 满足GDPR(通用数据保护条例)、数据删除功能。

2. 术语介绍

在深入研究 COW 和 MOR 之前,让我们先了解一下 Hudi 中使用的一些术语,以便更好地理解以下部分。

2.1 数据文件/基础文件

        Hudi将数据以列存格式(Parquet/ORC)存放,称为数据文件/基础文件,该列出格式是非常高效的并在整个行业中广泛使用,数据文件和基本文件通常可以互换使用,但两者的含义相同。

526c4fd6b68b6e4865f4bf3d92bdf62f.png

2.2 增量日志文件

        在 MOR 表格式中,更新被写入到增量日志文件中,该文件以 avro 格式存储。这些增量日志文件始终与基本文件相关联。假设有一个名为 data_file_1 的数据文件,对 data_file_1 中记录的任何更新都将写入到新的增量日志文件。在服务读取查询时,Hudi 将实时合并基础文件及其相应的增量日志文件中的记录。25ff2e29feabc3d576c977cf9f103872.png

2.3 文件组(FileGroup)

通常根据存储的数据量,可能会有很多数据文件。每个数据文件及其对应的增量日志文件形成一个文件组。在 COW 的情况下,它要简单得多,因为只有基本文件。

becf2f9d4aff8d719c92b48cb667dade.png

2.4 文件版本

我们以 COW 格式表为例来解释文件版本。每当数据文件发生更新时,将创建数据文件的较新版本,其中包含来自较旧数据文件和较新传入记录的合并记录。

 330921ad08e2cf690db718a44656ca2e.png

2.5 文件切片(FileSlice)

对于每个文件组,可能有不同的文件版本。因此文件切片由特定版本的数据文件及其增量日志文件组成。对于 COW,最新的文件切片是指所有文件组的最新数据/基础文件。对于 MOR,最新文件切片是指所有文件组的最新数据/基础文件及其关联的增量日志文件。

3、表存储类型:cow、mor

1、COW:写时复制(copy on write)

        写时复制(copy on write):仅使用列式文件(parquet)存储数据。在写入/更新数据时,直接同步合并原文件,生成新版本的基文件(需要重写整个列数据文件,即使只有一个字节的新数据被提交)。此存储类型下,写入数据非常昂贵,而读取的成本没有增加,所以适合频繁读的工作负载,因为数据集的最新版本在列式文件中始终可用,以进行高效的查询。

COW,他是在数据写入的时候,复制一份原来的拷贝,在其基础上添加新数据。正在读数据的请求,读取的是近的完整副本,这类似Mysql 的MVCC的思想。

        对 Hudi 的每一个新批次写入都将创建相应数据文件的新版本,新版本文件包括旧版本文件的记录以及来自传入批次的记录。

假设我们有 3 个文件组,其中包含如下数据文件。

8695e9d7dfa8eb7f8d0301109fe870ed.png

        我们进行一批新的写入,在索引后,我们发现这些记录与File group 1 和File group 2 匹配,然后有新的插入,我们将为其创建一个新的文件组(File group 4)。

dfe8ef8a0d463ee9d34832ef15cd0e05.png

        因此data_file1 和 data_file2 都将创建更新的版本,数据文件 1 V2 是数据文件 1 V1 的内容与数据文件 1 中传入批次匹配记录的记录合并。

由于在写入期间进行合并,COW 会产生一些写入延迟。但是COW 的优势在于它的简单性,不需要其他表服务(如压缩),也相对容易调试。

  • 优点 读取时,只读取对应分区的一个数据文件即可,较为高效
  • 缺点 数据写入的时候,需要复制一个先前的副本再在其基础上生成新的数据文件,这个过程比较耗时。且由于耗时,读请求读取到的数据相对就会滞后

2、MOR:读时合并(merge on read)

简称MOR。新插入的数据存储在delta log 中。定期再将delta log合并进行parquet数据文件。读取数据时,会将delta log跟老的数据文件做merge,得到完整的数据返回。当然,MOR表也可以像COW表一样,忽略delta log,只读取最近的完整数据文件。

MOR表写数据时,记录首先会被快速的写进日志文件,稍后会使用时间轴上的压缩操作将其与基础文件合并。根据查询是读取日志中的合并快照流还是变更流,还是仅读取未合并的基础文件,MOR表支持多种查询类型。在高层次上,MOR writer在读取数据时会经历与COW writer 相同的阶段。这些更新将追加到最新文件篇的最新日志文件中,而不会合并。

        读时合并(merge on read):使用列式(parquet)与行式(avro)文件组合,进行数据存储。在更新记录时,更新到增量文件中(avro),然后进行异步(或同步)的compaction,创建列式文件(parquet)的新版本。此存储类型适合频繁写的工作负载,因为新记录是以appending 的模式写入增量文件中。但是在读取数据集时,需要将增量文件与旧文件进行合并,生成列式文件。

顾名思义,合并成本从写入端转移到读取端。因此在写入期间我们不会合并或创建较新的数据文件版本。标记/索引完成后,对于具有要更新记录的现有数据文件,Hudi 创建增量日志文件并适当命名它们,以便它们都属于一个文件组。

23c6ec139ea892bea47293ec7c32c62a.png

        读取端将实时合并基本文件及其各自的增量日志文件。你可能会想到这种方式,每次的读取延迟都比较高(因为查询时进行合并),所 以 Hudi 使用压缩机制来将数据文件和日志文件合并在一起并创建更新版本的数据文件。

426d2ea6fa1327d8e3b7aed3331db6f7.png

        用户可以选择内联或异步模式运行压缩。Hudi也提供了不同的压缩策略供用户选择,最常用的一种是基于提交的数量。例如您可以将压缩的最大增量日志配置为 4。这意味着在进行 4 次增量写入后,将对数据文件进行压缩并创建更新版本的数据文件。压缩完成后,读取端只需要读取最新的数据文件,而不必关心旧版本文件。 

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

5. cow、mor对比 

5.1 写入延迟

正如我们之前所讨论,由于写入期间发生同步合并,与 MOR 相比COW 具有更高的写入延迟。

5.2 读取延迟

由于我们在 MOR 中进行实时合并,因此与 COW 相比MOR 往往具有更高的读取延迟。但是如果根据需求配置了合适的压缩策略,MOR 可以很好地发挥作用。

5.3 更新代价

由于我们为每批写入创建更新的数据文件,因此 COW 的 I/O 成本将更高。由于更新进入增量日志文件,MOR 的 I/O 成本非常低。

5.4 写放大

同样当我们创建更新版本的数据文件时,COW 会更高。假设您有一个大小为 100Mb 的数据文件,并且每次更新 10% 的记录进行 4 批写入,4 次写入后,Hudi 将拥有 5 个大小为 100Mb 的 COW 数据文件。你可以配置你的清理器(将在后面的博客中讨论)清理旧版本文件,但如果没有进行清理,最终会有 5 个版本的数据文件,总大小约500Mb。MOR 的情况并非如此,由于更新进入日志文件,写入放大保持在最低限度。对于上面的例子,假设压缩还没有开始,在 4 次写入后,我们将有 1x100Mb 的文件和 4 个增量日志文件(10Mb) 的大小约140Mb

Hudi机制

存储机制

hudi维护了一个时间轴,记录了在不同时刻对数据集进行的所有操作。
hudi拥有2种存储优化。
读优化(Copy On Write):在每次commit后都将最新的数据compaction成列式存储(parquet);
写优化(Merge On Read):对增量数据使用行式存储(avro),后台定期将它compaction成列式存储。

读数据

hudi维护着一个索引,以支持在记录key存在情况下,将新记录的key快速映射到对应的fileId。索引的实现是插件式的,默认是bloomFilter,也可以使用HBase。
hudi提供3种查询视图。
读优化视图:仅提供compaction后的列式存储的数据;
增量视图:仅提供一次compaction/commit前的增量数据;
实时视图:包括读优化的列式存储数据和写优化的行式存储数据。

更新数据

hudi写数据的时候需要指定PRECOMBINE_FIELD_OPT_KEY、RECORDKEY_FIELD_OPT_KEY和PARTITIONPATH_FIELD_OPT_KEY。
RECORDKEY_FIELD_OPT_KEY:每条记录的唯一id,支持多个字段;
PRECOMBINE_FIELD_OPT_KEY:在数据合并的时候使用到,当 RECORDKEY_FIELD_OPT_KEY 相同时,默认取 PRECOMBINE_FIELD_OPT_KEY 属性配置的字段最大值所对应的行;
PARTITIONPATH_FIELD_OPT_KEY:用于存放数据的分区字段。
hudi更新数据和插入数据很相似(写法几乎一样),更新数据时,会根据 RECORDKEY_FIELD_OPT_KEY、PRECOMBINE_FIELD_OPT_KEY 以及 PARTITIONPATH_FIELD_OPT_KEY三个字段对数据进行Merge。

 Hudi表的三个主要组件:

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

c609d9058ffa4e46a7af8d2a10f63d85.png

Hudi表数据结构

Hudi表的数据文件,可以使用操作系统的文件系统存储,也可以使用HDFS这种分布式的文件系统存储。为了后续分析性能和数据的可靠性,一般使用HDFS进行存储。以HDFS存储来看,一个Hudi表的存储文件分为两类。

cf4e002f38d70dad1d4e8012fbd3ea84.png

  • 包含_partition_key相关的路径是实际的数据文件,按分区存储,当然分区的路径key是可以指定的,我这里使用的是_partition_key
  • .hoodie 由于CRUD的零散性,每一次的操作都会生成一个文件,这些小文件越来越多后,会严重影响HDFS的性能,Hudi设计了一套文件合并机制。 .hoodie文件夹中存放了对应的文件合并操作相关的日志文件。

数据文件

Hudi真实的数据文件使用Parquet文件格式存储

cbeb7ec4eea78078aca62c07e7cf151e.png

.hoodie文件

Hudi把随着时间流逝,对表的一系列CRUD操作叫做Timeline。Timeline中某一次的操作,叫做Instant。Instant包含以下信息

  • Instant Action 记录本次操作是一次数据提交(COMMITS),还是文件合并(COMPACTION),或者是文件清理(CLEANS)
  • Instant Time 本次操作发生的时间
  • state 操作的状态,发起(REQUESTED),进行中(INFLIGHT),还是已完成(COMPLETED)

.hoodie文件夹中存放对应操作的状态记录

bb34667613e3c6feac6eecb14483f33f.png

Hudi存储管理

Hudi还对存储在Hudi数据集中的数据执行几个关键的存储管理功能。在DFS上存储数据的关键方面是管理文件大小和数量以及回收存储空间。
例如,HDFS在处理小文件上性能很差,这会对Name Node的内存及RPC施加很大的压力,并可能破坏整个集群的稳定性。
通常,查询引擎可在较大的列文件上提供更好的性能,因为它们可以有效地摊销获得列统计信息等的成本。
即使在某些云数据存储上,列出具有大量小文件的目录也常常比较慢。

以下是一些有效管理Hudi数据集存储的方法。

  • Hudi中的小文件处理功能,可以分析传入的工作负载并将插入内容分配到现有文件组中,
    而不是创建新文件组。新文件组会生成小文件。
  • 可以配置Cleaner来清理较旧的文件片,清理的程度可以调整,
    具体取决于查询所需的最长时间和增量拉取所需的回溯。
  • 用户还可以调整基础/parquet文件日志文件的大小
    和预期的压缩率,使足够数量的插入被分到同一个文件组中,最终产生大小合适的基础文件。
  • 智能调整批插入并行度,可以产生大小合适的初始文件组。
    实际上,正确执行此操作非常关键,因为文件组一旦创建后就不能删除,只能如前所述对其进行扩展。
  • 对于具有大量更新的工作负载,读取时合并存储提供了一种很好的机制,
    可以快速将其摄取到较小的文件中,之后通过压缩将它们合并为较大的基础文件。

二、Hudi与Kudu、Hive、 HBase对比

Hudi v.s. Kudu

Apache Kudu,需要单独部署集群。而Apache Hudi则不需要,它可以利用现有的大数据集群比如HDFS做数据文件存储,然后通过Hive做数据分析,相对来说更适合资源受限的环境。

Apache Kudu是一个与Hudi具有相似目标的存储系统,该系统通过对upserts支持来对PB级数据进行实时分析。 一个关键的区别是Kudu还试图充当OLTP工作负载的数据存储,而Hudi并不希望这样做。 因此,Kudu不支持增量拉取(Incremental Pulling)(截至2017年初),而Hudi支持以便进行增量处理。

Kudu与分布式文件系统抽象和HDFS完全不同,Kudu剥离了HDFS(Hadoop Distribute File System)及其分布式文件系统抽象接口,它自己的一组存储服务器通过RAFT相互通信,通过RAFT一致性算法管理自己的一组存储服务器。 与之不同的是,Hudi旨在与底层Hadoop兼容的文件系统(HDFS,S3或Ceph)一起使用,并且没有自己的存储服务器群,而是依靠Apache Spark来完成繁重的工作。 因此,Hudi可以像其他Spark作业一样轻松扩展,而Kudu则需要硬件和运营支持,特别是HBase或Vertica等数据存储系统。 到目前为止,我们还没有做任何直接的基准测试来比较Kudu和Hudi(鉴于RTTable正在进行中)。 但是,如果我们要使用CERN, 我们预期Hudi在摄取parquet上有更卓越的性能。

截止目前,尚没有一份官方的基准测试可以全面地评估两者的性能。

kudu

kudu的存储机制和hudi的写优化方式有些相似。kudu的最新数据保存在内存,称为MemRowSet(行式存储,基于primary key有序),当MemRowSet写满(默认1G或者120s)后flush到磁盘,形成DiskRowSet(列式存储)。

DiskRowSet包含baseData与DeltaStores两部分,DeltaStores包含一个DeltMemStore和多个DeltaFile,后续的更新数据存放在DeltMemStore中,增长到一定程度后flush成DeltaFile文件。

kudu会定期执行compaction操作,将DeltaFile中的更新合并到DiskRowSet,或者合并DiskRowSet,清除已删除的数据,并减少DiskRowSet的数量。

hudi

hudi维护了一个时间轴,记录了在不同时刻对数据集进行的所有操作。

hudi拥有2种存储优化,读优化适合读多写少的场景,写优化适合读少写多的场景。

  • 读优化(Copy On Write):在每次commit后都将最新的数据compaction成列式存储(parquet);
  • 写优化(Merge On Read):对增量数据使用行式存储(avro),后台定期将它compaction成列式存储。

读数据

kudu

先根据要扫描数据的主键范围,定位到目标的tablets,然后读取tablets中的DiskRowSet。

在读取每个DiskRowSet时,先根据主键过滤要scan的范围,然后加载范围内的baseData,再找到对应的DeltaStores,
应用所有变更,最后union上MemRowSet中的内容,最后返回数据给client。

kudu提供range分区和hash分区两种分区方式,通过多种索引(主键范围索引、bloomfilter、主键索引),支持随机读取数据和高效的批量读取数据。

 hudi

hudi也维护着一个索引,以此将key快速映射到对应的fileId。索引的实现是插件式的,默认是bloomFilter,也可以使用HBase。

hudi提供3种查询视图。

  • 读优化视图:仅提供compaction后的列式存储的数据;
  • 增量视图:仅提供一次compaction/commit前的增量数据;
  • 实时视图:包括列式存储数据和写优化的行式存储数据。

更新数据

 kudu

client向master发出请求,通过索引定位到具体的tablet,然后根据元数据连接tablet对应的tserver。

若数据在磁盘(DiskRowSet)上,则将更新信息写入DeltMemStore中,若数据在内存(MemRowSet)中,则将信息写入所在行的mutation链表中。

 hudi

hudi没有传统意义的更新,只有append和重写。

hudi写数据的时候需要指定以下3个key。

  • RECORDKEY_FIELD_OPT_KEY:每条记录的唯一id,支持多个字段;
  • PRECOMBINE_FIELD_OPT_KEY:在数据合并的时候使用到,当RECORDKEY_FIELD_OPT_KEY相同时,默认取PRECOMBINE_FIELD_OPT_KEY属性配置的字段最大值所对应的行;
  • PARTITIONPATH_FIELD_OPT_KEY:用于存放数据的分区字段。

hudi更新数据和插入数据很相似(写法几乎一样),更新数据时,会根据以上三个字段对数据进行Merge。

如何选择合适的存储方案? kudu/hudi

kudu

  1. 不同于hudi和delta lake是作为数据湖的存储方案,kudu设计的初衷是作为hive和hbase的折中,因此它同时具有随机读写和批量分析的特性。

  2. kudu允许对不同列使用单独的编码和压缩格式,拥有强大的索引支持,搭配range分区和hash分区的合理划分,
    对分区查看、扩容和数据高可用性的支持都非常好,适用于既有随机访问,也有批量数据扫描的复合场景。

  3. kudu可以和impala、spark集成,支持sql操作,除此之外,kudu能够充分发挥高性能存储设备的优势。

  4. 相比较其他两者,kudu不支持云存储,也不支持版本回滚和增量处理。

hudi

  1. hudi的产生背景是为了解决Uber的增量更新问题,它提供了丰富的视图和存储优化方式,
    可以适配批量访问场景,增量处理场景,以及近实时查询场景,无论是读多写少还是读少写多,hudi都能提供对应的优化方案,
    用户可以根据自身场景灵活选择合适的配置。

  2. 三者之中,hudi的兼容性最好,它原生支持spark、presto、hive和mapreduce等大数据生态系统,并且读写底层文件实现了自己的InputFormat,更容易与其它系统做兼容。

  3. hudi目前还不支持通过sql操作数据,(19年12月)社区已经将其作为下一步的方向,2121版本支持sql。

  4. hudi不存在锁机制,因此不支持多客户端同时写一张表,这是需要注意的一点。

Hudi v.s. Hive Transactions (Hive事务)/ ACID

Hive事务/ACID是另一项类似的工作,它试图实现在ORC文件格式之上的存储读取时合并(merge-on-read)的存储功能。 可以理解,此功能与Hive以及LLAP之类的其他工作紧密相关。 与Hudi相比,Hive Transactions不不支持读时优化(Read-Optimized)存储和增量拉取(Incremental Pulling)。 在实现选择方面,Hudi充分利用了类似Spark的处理框架的功能,而Hive事务特性则在用户或Hive Metastore启动的Hive任务/查询的下实现。根据Uber工程师的实际生产经验,与其他方法相比,将Hudi作为一个三方依赖库嵌入到现有的Spark管道中要容易得多,并且操作不会太繁琐。 Hudi还设计用于与Presto/Spark等非Hive引擎合作,并计划引入除parquet以外的文件格式。

Hudi v.s Hbase

虽然HBase是面向OLTP场景的键值存储(key-value store),典型的应用场景就是不断插入新的记录且不怎么修改。但由于本身运行于HDFS之上,用户往往倾向于在HBase做一些分析相关的业务。鉴于HBase经过大量写入优化,它支持开箱即用的亚秒级upsert,而Hive-on-HBase则允许用户查询该数据。但就分析类业务场景的实际性能而言,由于这类场景负载主要在读取上,像Parquet/ORC这样的混合列式存储格式轻松击败HBase,因为这些工作负载主要是读取繁重的工作。Hudi弥补了更快的数据与分析存储格式之间的差距。 Hudi打破了数据快速入库和基于该数据进行分析业务之间的壁障。从可操作性上来说,相比于Hbase需要管理一个含有大量Region Server的集群来满足分析性业务场景,而Hudi主需要一个三方依赖库就可以实现,可维护性和可扩展性更强,为用户提供可更快给出数据的库更具可扩展性。最后,和Hudi相比,HBase不支持增量处理原语,如commit timesincremental pull提交时间增量拉取之类的增量处理原语。

Hudi v.s. Stream Processing流式处理

一个普遍的问题:”Hudi与流处理系统有何关系?”,我们将在这里尝试回答。简而言之,Hudi可以与当今的批处理(写时复制存储)和流处理(读时合并存储)作业集成,以将计算结果存储在Hadoop中。 对于Spark应用程序,这可以通过将Hudi库与Spark/Spark Steaming的DAG直接集成来实现。在非Spark处理系统(例如Flink、Hive)情况下,可以在相应的系统中进行处理,然后通过Kafka Topics /HDFS中间文件将其发送到Hudi表中。从概念上讲,数据处理 管道仅由三个部分组成:source, processingsink,输入处理输出,用户最终针对输出运行查询以便使用管道的结果。Hudi可以充当将数据存储在DFS上的输入source或输出sink,前者读取存储在HDFS上的Hudi表,后者将数据写人存储于HDFS的Hudi表。

流式处理保存的Hudi表,最终交给Presto/Spark SQL/Hive做查询。

<think>首先,用户的问题是关于比较Hudi表和Hive表的语法差异。用户说:"我想比较Hudi表和Hive表的语法差异 请问Apache HudiApache Hive表的语法区别"。所以,我需要专注于解释Apache Hudi表和Apache Hive表在语法上的区别。 回顾系统级指令: - 所有行内数学表达式必须使用$...$格式,但这个问题是关于数据库表的语法,不是数学,所以可能不需要数学表达式。但如果有变量或类似的东西,我会用。 - 独立公式必须使用$$...$$格式,但同样,这里可能不适用。 - LaTeX语法正确:确保在需要时使用正确的LaTeX。 - 使用中文回答:整个回答要用中文。 - 生成相关问题:在回答结束时,添加相关问题。 - 回答中引用的段落末尾自然地添加引用标识:参考提供的引用内容,并在相关地方添加[^1]等。 用户可见层: - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠:基于我的知识和提供的引用。 提供的引用: - [^1]: 关于Hudi表映射到Hive外部表,用于实时视图等。 - [^2]: Hive集成Hudi的方法,复制jar文件。 - [^3]: Hudi案例实战,包括数据处理、保存Hudi表、集成Hive等。 - [^4]: Doris数据集成,通过Spark生成Hudi表,使用SQL创建Hudi表。 - [^5]: Spark Hudi同步到Hive的问题,自动同步MetaStore逻辑。 用户的问题是比较语法差异。所以,我需要比较在创建表、查询表等方面Hudi表和Hive表的语法差异。 关键点: - **Hive表**:Apache Hive是一个数据仓库工具,允许使用SQL-like语言(HiveQL)查询数据。语法类似SQL,用于处理结构化数据。 - **Hudi表**:Apache Hudi是一个数据湖技术,支持增量处理和实时数据摄取。Hudi表可以存储在HDFS或云存储上,并通过Hive或其他工具查询。 语法差异: 1. **创建表**: - Hive:使用标准的`CREATE TABLE`语句,指定列、分区等。 - Hudi:在创建表时,需要指定额外的属性,如`type`(COW或MOR)、`primaryKey`、`preCombineField`等。参考[^4],它展示了使用Spark SQL创建Hudi表:`CREATE TABLE ... USING hudi TBLPROPERTIES (type = 'cow', primaryKey = '...', ...)`。 2. **查询表**: - Hive:使用`SELECT`语句直接查询。 - HudiHudi表可以通过Hive查询,但需要先集成。参考[^1],Hudi表可以映射为Hive外部表,然后使用Hive查询。查询语法本身可能类似,但Hudi支持实时视图和增量查询。 3. **集成**: - Hive表是独立的;Hudi表需要额外的设置来Hive集成。参考[^2],需要复制jar文件到Hive lib下。 - Hudi表可以同步元数据到Hive Metastore,参考[^5]。 4. **其他操作**: - Hudi支持增量更新和删除,而Hive传统上不支持(除非使用ACID表)。 - 在Hudi中,表类型(如Copy-on-Write或Merge-on-Read)影响语法和行为。 结构回答: - 逐步解释:先介绍HudiHive,然后比较关键语法方面。 - 使用清晰的结构:用标题或列表。 - 添加引用:在相关部分添加[^1]等。 生成相关问题: - 在结束时,添加3-5个相关问题。 最终回答大纲: 1. 介绍HudiHive。 2. 比较创建表语法。 3. 比较查询语法。 4. 讨论集成和元数据同步。 5. 总结关键差异。 6. 添加相关问题。</think>### Apache HudiApache Hive表的语法区别比较 Apache HudiHadoop Upserts Deletes and Incrementals)是一个数据湖技术,支持实时增量数据处理和高效更新;而Apache Hive是一个传统的数据仓库工具,基于Hadoop提供SQL-like查询功能。两者在语法上存在显著差异,主要体现在表创建、查询、集成等方面。以下我将从关键语法维度进行逐步比较,确保内容真实可靠,并基于实际使用场景。 #### 1. **创建表语法** - **Hive表**: - 使用标准SQL语法创建表,类似于关系型数据库。 - 语法示例: ```sql CREATE TABLE hive_table ( id INT, name STRING, age INT ) PARTITIONED BY (country STRING) STORED AS ORC; ``` - 特点:无需指定特殊属性,分区和存储格式(如ORC、Parquet)是可选配置。Hive表主要处理静态数据,不支持原生增量更新。 - **Hudi表**: - 语法必须包含Hudi特有属性(TBLPROPERTIES),如`type`(表类型)、`primaryKey`(主键)和`preCombineField`(冲突解决字段)。 - 语法示例(通过Spark SQL创建,参考引用[^4]): ```sql CREATE TABLE hudi_table USING hudi TBLPROPERTIES ( type = 'cow', -- 表类型:cow(Copy-on-Write)或mor(Merge-on-Read) primaryKey = 'id', -- 指定主键 preCombineField = 'timestamp' -- 用于合并操作的字段 ) PARTITIONED BY (country) AS SELECT * FROM source_table; ``` - 特点: - 必须使用`USING hudi`声明表引擎。 - 属性`type`定义表的行为:`cow`支持直接覆盖写入,`mor`支持合并读取,适合实时场景。 - 分区语法类似Hive,但主键和预合并字段是强制性的,以支持Hudi的增量更新和删除功能。 **关键差异**:Hudi表创建时需指定额外属性来支持增量处理,而Hive表更简洁,侧重于静态数据管理。 #### 2. **查询表语法** - **Hive表**: - 使用标准SQL查询,无特殊语法。 - 示例: ```sql SELECT * FROM hive_table WHERE country = 'China'; ``` - 特点:查询语法ANSI SQL兼容,支持复杂Join和聚合操作,但数据延迟较高。 - **Hudi表**: - 查询语法Hive类似,但需通过Hive外部表映射实现(参考引用[^1])。 - 示例(查询Hudi表的实时视图): ```sql -- 先创建Hive外部表映射 CREATE EXTERNAL TABLE hudi_hive_view STORED BY 'org.apache.hudi.hadoop.HoodieParquetInputFormat' LOCATION '/path/to/hudi_table'; -- 然后查询 SELECT * FROM hudi_hive_view WHERE country = 'China'; ``` - 特点: - Hudi支持三种视图查询(通过Hive集成): - **实时视图**(Snapshot Query):获取最新数据,语法同Hive。 - **增量视图**(Incremental Query):使用`hoodie.<table>.consume.mode=incremental`属性查询增量数据(参考引用[^3])。 - **读优化视图**(Read Optimized Query):针对`mor`表优化读取。 - 增量查询示例: ```sql SET hoodie.hudi_table.consume.mode=incremental; SET hoodie.hudi_table.consume.start.timestamp=20230101000000; SELECT * FROM hudi_hive_view; ``` **关键差异**:Hudi查询需依赖Hive集成和特殊属性来支持增量功能,而Hive查询是标准化的。Hudi的增量视图语法是独有的,用于实时数据处理。 #### 3. **集成元数据同步** - **Hive表**: - 元数据存储在Hive Metastore中,无需额外同步。 - 语法简单:创建表后即可查询。 - **Hudi表**: - 必须Hive集成才能查询,语法涉及元数据同步步骤(参考引用[^2]和引用[^5])。 - 集成步骤: 1. 复制Hudi Jar到Hive Lib:`cp hudi-hadoop-mr-bundle.jar $HIVE_HOME/lib` [^2]。 2. 创建Hive外部表映射Hudi数据路径。 3. 启用自动元数据同步(在Hudi 0.6.0+支持,参考引用[^5]): ```sql SET hoodie.metasync.enabled=true; -- 自动同步元数据到Hive ``` - 特点:语法复杂,需手动或自动同步元数据,否则Hive无法识别Hudi表。Hudi的表位置(LOCATION)指向HDFS路径,而Hive表由Metastore管理。 **关键差异**:Hudi依赖额外配置和同步语法来实现Hive的互操作,而Hive表是自包含的。 #### 4. **数据操作语法(插入、更新、删除)** - **Hive表**: - 传统语法,不支持高效更新/删除(除非使用ACID表)。 - 示例(更新需全表重写): ```sql INSERT OVERWRITE TABLE hive_table PARTITION (country='China') SELECT id, name, age FROM source WHERE condition; ``` - **Hudi表**: - 支持增量Upserts和Deletes,语法使用Hudi特定API或Spark/Flink集成。 - 示例(通过Spark进行增量更新,参考引用[^3]): ```scala // Spark代码示例 val hudiOptions = Map( "hoodie.table.name" -> "hudi_table", "hoodie.datasource.write.operation" -> "upsert" ) dataframe.write.format("hudi").options(hudiOptions).mode("append").save("/path/to/hudi_table") ``` - 特点:语法非纯SQL,需借助编程接口或工具(如Spark),Hudi原生支持`MERGE INTO`语法在部分版本中。 **关键差异**:Hudi提供增量操作语法,优化了数据变更效率;Hive语法更简单但效率低。 #### 总结关键语法差异 | 维度 | Hive表语法 | Hudi表语法 | 核心差异原因 | |--------------|----------------------------------------|-----------------------------------------------|----------------------------------| | **创建表** | 标准`CREATE TABLE`,无特殊属性 | 必须包含`USING hudi`和`TBLPROPERTIES` | Hudi需要属性支持增量处理 | | **查询** | 直接`SELECT`,无增量支持 | 需Hive映射+属性(如`hoodie.consume.mode`) | Hudi提供实时/增量视图 | | **集成** | 自包含,无需同步 | 需手动复制Jar+配置同步(如`hoodie.metasync`) | Hudi依赖Hive进行元数据管理 | | **数据操作** | 有限更新(依赖重写) | 支持`upsert`/`delete`(通过API) | Hudi优化了数据变更效率 | Hudi表语法更复杂但适合实时场景,而Hive表语法简单适合批处理。实际应用中,HudiHive集成,以结合两者优势(参考引用[^1]和引用[^3])[^1][^3]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

四月天03

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值