基于 Apache Hudi 极致查询优化的探索实践
摘要:本文主要介绍 Presto 如何更好的利用 Hudi 的数据布局、索引信息来加速点查性能。
本文分享自华为云社区《华为云基于 Apache Hudi 极致查询优化的探索实践!》,作者:FI_mengtao。
背景
湖仓一体(LakeHouse)是一种新的开放式架构,它结合了数据湖和数据仓库的最佳元素,是当下大数据领域的重要发展方向。
华为云早在2020年就开始着手相关技术的预研,并落地在华为云 FusionInsight MRS智能数据湖解决方案中。
目前主流的三大数据湖组件 Apache Hudi、Iceberg、Delta各有优点,业界也在不断探索选择适合自己的方案。
华为湖仓一体架构核心基座是 Apache Hudi,所有入湖数据都通过 Apache Hudi 承载,对外通过 HetuEngine(Presto增强版)引擎承担一站式SQL分析角色,因此如何更好的结合 Presto 和 Hudi 使其查询效率接近专业的分布式数仓意义重大。查询性能优化是个很大的课题,包括索引、数据布局、预聚合、统计信息、引擎 Runtime优化等等。本文主要介绍 Presto 如何更好的利用 Hudi 的数据布局、索引信息来加速点查性能。预聚合和统计信息我们将在后续分享。
数据布局优化
大数据分析的点查场景一般都会带有过滤条件,对于这种类型查询,如果目标结果集很小,理论上我们可以通过一定手段在读取表数据时大量跳过不相干数据,只读取很小的数据集,进而显著的提升查询效率。我们可以把上述技术称之为 DataSkipping。
好的数据布局可以使相关数据更加紧凑(当然小文件问题也一并处理掉了)是实现 DataSkipping的关键一步。日常工作中合理设置分区字段、数据排序都属于数据布局优化。当前主流的查询引擎 Presto/Spark 都可以对Parquet文件做 Rowgroup 级别过滤,最新版本甚至支持 Page 级别的过滤;选取合适的数据布局方式可以使引擎在读取上述文件可以利用列的统计信息轻易过滤掉大量 Rowgroup/Page,进而减少IO。
那么是不是 DataSkipping仅仅依赖数据布局就好了?其实不然。上述过滤还是要打开表里每一个文件才能完成过滤,因此过滤效果有限,数据布局优化配合 FileSkipping才能更好的发挥效果。
当我们完成数据布局后,对每个文件的相关列收集统计信息,下图给个简单的示例,数据经过排序后写入表中生成三个文件,指定点查 where a < 10 下图可以清楚的看出 a < 10的结果集只存在于 parquet1文件中,parquet2/parquet3 中 a 的最小值都比10大,显然不可能存在结果集,所以直接裁剪掉 parquet2和 parquet3即可。

这就是一个简单 FileSkipping,FileSkipping的目的在于尽最大可能裁剪掉不需要的文件,减少扫描IO,实现 FileSkipping有很多种方式,例如
min-max统计信息过滤、BloomFilter、Bitmap、二级索引等等,每种方式都各有优缺点,其中 min-max 统计信息过滤最为常见,也是 Hudi/Iceberg/DeltaLake 默认提供的实现方式。
Apache Hudi核心能力
Clustering
Hudi早在 0.7.0 版本就已经提供了 Clustering 优化数据布局,0.10.0 版本随着 Z-Order/Hilbert高阶聚类算法加入,Hudi的数据布局优化日趋强大,Hudi 当前提供以下三种不同的聚类方式,针对不同的点查场景,可以根据具体的过滤条件选择不同的策略

关于 Z-Order、Hilbert 具体原理可以查阅相关Wiki,https://en.wikipedia.org/wiki/Z-order 本文不再详细赘述。
Metadata Table(MDT)
Metadata Table(MDT):Hudi的元数据信息表,是一个自管理的 Hudi MoR表,位于 Hudi 表的 .hoodie目录,开启后用户无感知。同样的 Hudi 很早就支持 MDT,经过不断迭代 0.12版本 MDT 已经成熟,当前 MDT 表已经具备如下能力
(1)Column_stats/Bloomfilter
上文我们介绍了数据布局优化,接下来说说 Hudi 提供的 FileSkipping能力。当前 Hudi 支持对指定列收集包括min-max value,null count,total count 在内的统计信息,并且 Hudi 保证这些信息收集是原子性,利用这些统计信息结合查询引擎可以很好的完成 FileSkipping大幅度减少IO。BloomFilter是 Hudi 提供的另一种能力,当前只支持对主键构建 BloomFilter。BloomFilter判断不存在就一定不存在的特性,可以很方便进行 FileSkipping,我们可以将查询条件直接作用到每个文件的 BloomFilter 上,进而过滤点无效的文件,注意 BloomFilter 只适合等值过滤条件例如where a = 10,对于 a > 10这种就无能为力。
(2)高性能FileList
在查询超大规模数据集时,FileList是不可避免的操作,在 HDFS 上该操作耗时还可以接受,一旦涉及到对象存储,大规模 FileList 效率极其低下,Hudi 引入 MDT 将文件信息直接保存在下来,从而避免了大规模FileList。

Presto 与 Hudi的集成
HetuEngine(Presto)作为数据湖对外出口引擎,其查询 Hudi 能力至关重要。对接这块我们主要针对点查和复杂查询做了不同的优化,下文着重介绍点查场景。在和 Hudi 集成之前首先要解决如下问题
- 如何集成 Hudi,在 Hive Connector 直接魔改,还是使用独立的 Hudi Connector?
- 支持哪些索引做 DataSkipping?
- DataSkipping 在 Coordinator 侧做还是在 Worker 端做?
问题1: 经过探讨我们决定使用 Hudi Connector承载本次优化。当前社区的 Connector 还略优不足,缺失一些优化包括统计信息、Runtime Filter、Filter不能下推等导致 TPC-DS 性能不是很理想,我们在本次优化中重点优化了这块,后续相关优化会推给社区。
问题2: 内部 HetuEngine 其实已经支持 Bitmap 和二级索引,本次重点集成了 MDT 的 Column statistics和 BloomFilter 能力,利用 Presto下推的 Filter 直接裁剪文件。
问题3: 关于这个问题我们做了测试,对于 column 统计信息来说,总体数据量并不大,1w 个文件统计信息大约几M,加载到 Coordinator 内存完全没有问题,因此选择在 Coordinator 侧直接做过滤。

对于 BloomFilter、Bitmap 就完全不一样了,测试结果表明 1.4T 数据产生了 1G 多的 BloomFilter 索引,把这些索引加载到 Coordinator 显然不现实。我们知道 Hudi MDT 的 BloomFilter 实际是存在 HFile里,HFile点查十分高效,因此我们将 DataSkipping 下压到 Worker 端,每个 Task 点查 HFile 查出自己的 BloomFilter 信息做过滤。

点查场景测试
测试数据
我们采用和 ClickHouse 一样的SSB数据集进行测试,数据规模1.5T,120亿条数据。
$ ./dbgen -s 2000 -T c
$ ./dbgen -s 2000 -T l
$ ./dbgen -s 2000 -T p
$ ./dbgen -s 2000 -T s
测试环境
1CN+3WN Container 170GB,136GB JVM heap, 95GB Max Query Memory,40vcore
数据处理
利用 Hudi 自带的 Hilbert 算法直接预处理数据后写入目标表,这里 Hilbert 算法指定 S_CITY,C_CITY,P_BRAND, LO_DISCOUNT作为排序列。
SpaceCurveSortingHelper
.orderDataFrameBySamplingValues(df.withColumn("year", expr("year((LO_ORDERDATE))")), LayoutOptimizationStrategy.HILBERT, Seq("S_CITY", "C_CITY", "P_BRAND", "LO_DISCOUNT"), 9000)
.registerTempTable("hilbert")
spark.sql("insert into lineorder_flat_parquet_hilbert select * from hilbert")
测试结果
使用冷启动方式,降低 Presto 缓存对性能的影响。
SSB Query

文件读取量
- 对于所有 SQL 我们可以看到 2x - 11x 的性能提升, FileSkipping 效果更加明显过滤掉的文件有 2x - 200x 的提升。
- 即使没有 MDT ,Presto 强大的 Rowgroup 级别过滤,配合 Hilbert 数据布局优化也可以很好地提升查询性能。
- SSB模型扫描的列数据都比较少, 实际场景中如果扫描多个列 Presto + MDT+ Hilbert 的性能可以达到 30x 以上。
- 测试中同样发现了MDT的不足,120亿数据产生的MDT表有接近50M,加载到内存里面需要一定耗时,后续考虑给MDT配置缓存盘加快读取效率。
关于 BloomFilter 的测试,由于 Hudi 只支持对主键构建 BloomFilter,因此我们构造了1000w 数据集做测试
spark.sql(
"""
|create table prestoc(
|c1 int,
|c11 int,
|c12 int,
|c2 string,
|c3 decimal(38, 10),
|c4 timestamp,
|c5 int,
|c6 date,
|c7 binary,
|c8 int
|) using hudi
|tblproperties (
|primaryKey = 'c1',
|preCombineField = 'c11',
|hoodie.upsert.shuffle.parallelism = 8,
|hoodie.table.keygenerator.class = 'org.apache.hudi.keygen.SimpleKeyGenerator',
|hoodie.metadata.enable = "true",
|hoodie.metadata.index.column.stats.enable = "true",
|hoodie.metadata.index.column.stats.file.group.count = "2",
|hoodie.metadata.index.column.stats.column.list = 'c1,c2',
|hoodie.metadata.index.bloom.filter.enable = "true",
|hoodie.metadata.index.bloom.filter.column.list = 'c1',
|hoodie.enable.data.skipping = "true",
|hoodie.cleaner.policy.failed.writes = "LAZY",
|hoodie.clean.automatic = "false",
|hoodie.metadata.compact.max.delta.commits = "1"
|)
|
|""".stripMargin)
最终一共产生了8个文件,结合 BloomFilter Skipping掉了7 个,效果非常明显。
后续工作
后续关于点查这块工作会重点关注 Bitmap 以及二级索引。最后总结一下 DataSkipping 中各种优化技术手段的选择方式。
- Clustering中各种排序方式需要结合 Column statistics 才能达到更好的效果。
- BloomFilter 适合等值条件点查,不需要数据做排序, 但是要选择高基字段,低基字段 BloomFIlter 用处不大;另外超高基也不要选 BloomFilter,产出的 BloomFilter 结果太大。
基于 Apache Hudi 极致查询优化的探索实践的更多相关文章
- 华为云 MRS 基于 Apache Hudi 极致查询优化的探索实践
背景 湖仓一体(LakeHouse)是一种新的开放式架构,它结合了数据湖和数据仓库的最佳元素,是当下大数据领域的重要发展方向. 华为云早在2020年就开始着手相关技术的预研,并落地在华为云 Fusio ...
- Robinhood基于Apache Hudi的下一代数据湖实践
1. 摘要 Robinhood 的使命是使所有人的金融民主化. Robinhood 内部不同级别的持续数据分析和数据驱动决策是实现这一使命的基础. 我们有各种数据源--OLTP 数据库.事件流和各种第 ...
- 基于Apache Hudi构建数据湖的典型应用场景介绍
1. 传统数据湖存在的问题与挑战 传统数据湖解决方案中,常用Hive来构建T+1级别的数据仓库,通过HDFS存储实现海量数据的存储与水平扩容,通过Hive实现元数据的管理以及数据操作的SQL化.虽然能 ...
- 基于 Apache Hudi 和DBT 构建开放的Lakehouse
本博客的重点展示如何利用增量数据处理和执行字段级更新来构建一个开放式 Lakehouse. 我们很高兴地宣布,用户现在可以使用 Apache Hudi + dbt 来构建开放Lakehouse. 在深 ...
- 触宝科技基于Apache Hudi的流批一体架构实践
1. 前言 当前公司的大数据实时链路如下图,数据源是MySQL数据库,然后通过Binlog Query的方式消费或者直接客户端采集到Kafka,最终通过基于Spark/Flink实现的批流一体计算引擎 ...
- 字节跳动基于Apache Hudi构建EB级数据湖实践
来自字节跳动的管梓越同学一篇关于Apache Hudi在字节跳动推荐系统中EB级数据量实践的分享. 接下来将分为场景需求.设计选型.功能支持.性能调优.未来展望五部分介绍Hudi在字节跳动推荐系统中的 ...
- 基于Apache Hudi + Flink的亿级数据入湖实践
本次分享分为5个部分介绍Apache Hudi的应用与实践 实时数据落地需求演进 基于Spark+Hudi的实时数据落地应用实践 基于Flink自定义实时数据落地实践 基于Flink+Hudi的应用实 ...
- Uber基于Apache Hudi构建PB级数据湖实践
1. 引言 从确保准确预计到达时间到预测最佳交通路线,在Uber平台上提供安全.无缝的运输和交付体验需要可靠.高性能的大规模数据存储和分析.2016年,Uber开发了增量处理框架Apache Hudi ...
- OnZoom 基于Apache Hudi的流批一体架构实践
1. 背景 OnZoom是Zoom新产品,是基于Zoom Meeting的一个独一无二的在线活动平台和市场.作为Zoom统一通信平台的延伸,OnZoom是一个综合性解决方案,为付费的Zoom用户提供创 ...
随机推荐
- 最佳实践 | 疫情之下,了解 eolink 如何助力远程办公!
新冠疫情肆虐,eolink 结合自身长期的远程研发协作经验,为企业推出 API 管理远程协作指南,以下方案不仅在 Eolinker 内部,也在众多客户中得到验证,希望能够帮助您快速了解如何将 API ...
- Solution -「CF113D」Museum
Upd 2021.10.21 更改了状态定义. 记 \(S(u)\) 表示 \(u\) 结点的相邻结点的集合. 又记 \(p(u)\) 表示走到了 \(u\) 且下一步继续留在 \(u\) 结点的概率 ...
- Java代码优化的30个小技巧
前言 我之前写过两篇关于优化相关的问题:<聊聊sql优化的15个小技巧>和<聊聊接口性能优化的11个小技巧>,发表之后,在全网受到广大网友的好评.阅读量和点赞率都很高,说明了这 ...
- Java开发学习(十四)----Spring整合Mybatis及Junit
一.Spring整合Mybatis思路分析 1.1 环境准备 步骤1:准备数据库表 Mybatis是来操作数据库表,所以先创建一个数据库及表 create database spring_db cha ...
- 利用Kaptcha.jar生成图片验证码(以下源码可以直接复制并自定义修改)
说明:Kaptcha是一个很实用的验证码生成工具,它可以生成各种样式的验证码,因为它是可以配置的 目录: 一 实现步骤 二 实例 A 编写jsp页面 B 配置web.xml C 验证输入正确与否. 一 ...
- AtCoder Beginner Contest 260 E // 双指针 + 差分
题目传送门:E - At Least One (atcoder.jp) 题意: 给定大小为N的两个数组A,B,求长度分别为1~M的满足以下条件的连续序列数量,条件为: 对于每个i(从1~N),Ai和B ...
- angular里forwardRef的原理
一段会报错的angular代码 @Injectable() class Socket { constructor(private buffer: Buffer) { } } console.log(B ...
- PHP及相关服务器防盗链
服务器防盗链 假设域名为www.localhost.com 1.apache配置httpd.conf SetEnvIfNoCase Referer "^http://www.localhos ...
- 【Meetup回顾】Apache DolphinScheduler在联通的实践和二次开发经验分享
在由 openLooKeng 社区主办,Apahce DolphinScheduler社区.Apache Pulsar 社区.示说网协办的联合 Meetup 上,来自联通数字科技的王兴杰老师分享了Do ...
- (防坑)Alphafold 非docker 安装指南
本指南适用于Linux系统.Alphafold官方也强调尽量使用Linux系统!官方提供了docker版安装步骤. Alphafold简介: 强大的蛋白质结构预测. 开源地址:https://gith ...