Hudi 在 vivo 湖仓一体的落地实践

作者:vivo 互联网大数据团队 - Xu Yu

在增效降本的大背景下,vivo大数据基础团队引入Hudi组件为公司业务部门湖仓加速的场景进行赋能。主要应用在流批同源、实时链路优化及宽表拼接等业务场景。

一、Hudi 基础能力及相关概念介绍

1.1 流批同源能力

与Hive不同,Hudi数据在Spark/Flink写入后,下游可以继续使用Spark/Flink引擎以流读的形式实时读取数据。同一份Hudi数据源既可以批读也支持流读。

Flink、Hive、Spark的流转批架构:

图片

Hudi流批同源架构:

图片

1.2 COW和MOR的概念

Hudi支持COW(Copy On Write)和MOR(Merge On Read)两种类型:

(1)COW写时拷贝

每次更新的数据都会拷贝一份新的数据版本出来,用户通过最新或者指定version的可以进行数据查询。缺点是写入的时候往往会有写内存放大的情况,优点是查询不需要合并,直接读取效率相对比较高。JDK中的CopyOnWriteArrayList/CopyOnWriteArraySet 容器正是采用了 COW 思想。

COW表的数据组织格式如下:

图片

(2)MOR读时合并:

每次更新或者插入新的数据时,并写入parquet文件,而是写入Avro格式的log文件中,数据按照FileGroup进行分组,每个FileGroup由base文件(parquet文件)和若干log文件组成,每个FileGroup有单独的FileGroupID;在读取的时候会在内存中将base文件和log文件进行合并,进而返回查询的数据。缺点是合并需要花费额外的合并时间,查询的效率受到影响;优点是写入的时候效率相较于COW快很多,一般用于要求数据快速写入的场景。

MOR数据组织格式如下:

图片

1.3 Hudi的小文件治理方案

Hudi表会针对COW和MOR表制定不同的文件合并方案,分别对应Clustering和Compaction。

Clustering顾名思义,就是将COW表中多个FileGroup下的parquet根据指定的数据大小重新编排合并为新的且文件体积更大的文件块。如下图所示:

图片

Compaction即base parquet文件与相同FileGroup下的其余log文件进行合并,生成最新版本的base文件。如下图所示:

图片

1.4 周边引擎查询Hudi的原理

当前主流的OLAP引擎等都是从HMS中获取Hudi的分区元数据信息,从InputFormat属性中判断需要启动HiveCatalog还是HudiCatalog,然后生成查询计划最终执行。当前StarRocks、Presto等引擎都支持以外表的形式对Hudi表进行查询。

图片

1.5 Procedure介绍

Hudi 支持多种Procedure,即过程处理程序,用户可以通过这些Procedure方便快速的处理Hudi表的相关逻辑,比如Compaction、Clustering、Clean等相关处理逻辑,不需要进行编码,直接通过sparksql的语句来执行。

1.6 项目架构

1. 按时效性要求进行分类

秒级延迟:

图片

分钟级延迟:

图片

当前Hudi主要还是应用在准实时场景

上游从Kafka以append模式接入ods的cow表,下游部分dw层业务根据流量大小选择不同类型的索引表,比如bucket index的mor表,在数据去重后进行dw构建,从而提供统一数据服务层给下游的实时和离线的业务,同时ods层和dw层统一以insert overwrite的方式进行分区级别的容灾保障,Timeline上写入一个replacecommit的instant,不会引发下游流量骤增,如下图所示:

图片

1.7 线上达成能力

实时场景:

支持1亿条/min量级准实时写入;流读延迟稳定在分钟级

离线场景:

支持千亿级别数据单批次离线写入;查询性能与查询Hive持平(部分线上任务较查询Hive提高20%以上)

小文件治理:

95%以上的合并任务单次执行控制在10min内完成

二、组件能力优化

2.1 组件版本

当前线上所有Hudi的版本已从0.12 升级到 0.14,主要考虑到0.14版本的组件能力更加完备,且与社区前沿动态保持一致。

2.2 流计算场景

1. 限流

数据积压严重的情况下,默认情况会消费所有未消费的commits,往往因消费的commits数目过大,导致任务频繁OOM,影响任务稳定性;优化后每次用户可以摄取指定数目的commits,很大程度上避免任务OOM,提高了任务稳定性。

图片

2. 外置clean算子

避免单并行度的clean算子最终阶段影响数据实时写入的性能;将clean单独剥离到

compaction/clustering执行。这样的好处是单个clean算子,不会因为其生成clean计划和执行导致局部某些Taskmanager出现热点的问题,极大程度提升了实时任务稳定性。

图片

3. JM内存优化

部分大流量场景中,尽管已经对Hudi进行了最大程度的调优,但是JM的内存仍然在较高水位波动,还是会间隔性出现内存溢出影响稳定性。这种情况下我们尝试对 state.backend.fs.memory-threshold 参数进行调整;从默认的20KB调整到1KB,JM内存显著下降;同时运行至今state相关数据未产生小文件影响。

图片

2.3 批计算场景

1. Bucket index下的BulkInsert优化

0.14版本后支持了bucket表的bulkinsert,实际使用过程中发现分区数很大的情况下,写入延迟耗时与计算资源消耗较高;分析后主要是打开的句柄数较多,不断CPU IO 频繁切换影响写入性能。

因此在hudi内核进行了优化,主要是基于partition path和bucket id组合进行预排序,并提前关闭空闲写入句柄,进而优化cpu资源使用率。

这样原先50分钟的任务能降低到30分钟以内,数据写入性能提高约30% ~ 40%。

优化前:

图片

优化后:

图片

2. 查询优化

0.14版本中,部分情况下分区裁剪会失效,从而导致条件查询往往会扫描不相关的分区,在分区数庞大的情况下,会导致driver OOM,对此问题进行了修复,提高了查询任务的速度和稳定性。

eg:select * from `hudi_test`.`tmp_hudi_test` where day='2023-11-20' and hour=23; 

(其中tmp_hudi_test是一张按日期和小时二级分区的表)

修复前:

图片

修复后:

图片

优化后不仅包括减少分区的扫描数目,也减少了一些无效文件RPC的stage。

3. 多种OLAP引擎支持

此外,为了提高MOR表管理的效率,我们禁止了RO/RT表的生成;同时修复了原表的元数据不能正常同步到HMS的缺陷(这种情况下,OLAP引擎例如Presto、StarRocks查询原表数据默认仅支持对RO/RT表的查询,原表查询为空结果)。

图片

2.4 小文件合并

1. 序列化问题修复

0.14版本Hudi在文件合并场景中,Compaction的性能相较0.12版本有30%左右的资源优化,比如:原先0.12需要6G资源才能正常启动单个executor的场景下,0.14版本 4G就可以启动并稳定执行任务;但是clustering存在因TypedProperties重复序列化导致的性能缺陷。完善后,clustering的性能得到30%以上的提升。

可以从executor的修复前后的火焰图进行比对。

修复前:

图片

修复后:

图片

2. 分批compaction/clustering

compaction/clustering默认不支持按commits数分批次执行,为了更好的兼容平台调度能力,对compaction/clustering相关procedure进行了改进,支持按批次执行。

同时对其他部分procedure也进行了优化,比如copy_to_table支持了列裁剪拷贝、delete_procedures支持了批量执行等,降低sparksql的执行时间。

3. clean优化

Hudi0.14 在多分区表的场景下clean的时候很容易OOM,主要是因为构建HoodieTableFileSystemView的时候需要频繁访问TimelineServer,因产生大量分区信息请求对象导致内存溢出。具体情况如下:

图片

对此我们对partition request Job做了相关优化,将多个task分为多个batch来执行,降低对TimelineSever的内存压力,同时增加了请求前的缓存判断,如果已经缓存的将不会发起请求。

改造后如下:

图片

此外实际情况下还可以在FileSystemViewManager构建过程中将 remoteview 和 secondview 的顺序互调,绝大部分场景下也能避免clean oom的问题,直接优先从secondview中获取分区信息即可。

2.5 生命周期管理

当前计算平台支持用户表级别生命周期设置,为了提高删除的效率,我们设计实现了直接从目录对数据进行删除的方案,这样的收益有:

  1. 降低了元数据交互时间,执行时间快;

  2. 无须加锁、无须停止任务;

  3. 不会影响后续compaction/clustering 相关任务执行(比如执行合并的时候不会报文件不存在等异常)。

删除前会对compaction/clustering等instants的元数据信息进行扫描,经过合法性判断后区分用户需要删除的目录是否存在其中,如果有就保存;否则直接删除。流程如下:

图片

三、总结

我们分别在流批场景、小文件治理、生命周期管理等方向做了相关优化,上线后的收益主要体现这四个方向:

  1. 部分实时链路可以进行合并,降低了计算和存储资源成本;

  2. 基于watermark有效识别分区写入的完成度,接入湖仓的后续离线任务平均SLA提前时间不低于60分钟;

  3. 部分流转批后的任务上线后执行时间减少约40%(比如原先执行需要150秒的任务可以缩短到100秒左右完成 ;

  4. 离线增量更新场景,部分任务相较于原先Hive任务可以下降30%以上的计算资源。

同时跟进用户实际使用情况,发现了一些有待优化的问题:

  1. Hudi生成文件的体积相较于原先Hive,体积偏大(平均有1.3 ~ 1.4的比例);

  2. 流读的指标不够准确;

  3. Hive—>Hudi迁移需要有一定的学习成本;

针对上述问题,我们也做了如下后续计划:

  1. 对hoodie parquet索引文件进行精简优化,此外业务上对主键的重新设计也会直接影响到文件体积大小;

  2. 部分流读的指标不准,我们已经完成初步的指标修复,后续需要补充更多实时的任务指标来提高用户体验;

  3. 完善Hudi迁移流程,提供更快更简洁的迁移工具,此外也会向更多的业务推广Hudi组件,进一步挖掘Hudi组件的潜在使用价值。

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

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

相关文章

大数据-MapReduce-关于Json数据格式的数据的处理与练习

目录 🐶16.1 Json是什么? 🐶16.2 Json的语法规则 🐶16.3 JSON 和 XML 🐶16.4 java中关于json中的jar包 🥙GSON:由谷歌进行开发 🥙Fastjson: 由阿里进行开发 &…

go学习redis的学习与使用

文章目录 一、redis的学习与使用1.Redis的基本介绍2.Redis的安装下载安装包即可3.Redis的基本使用1)Redis的启动:2)Redis的操作的三种方式3)说明:Redis安装好后,默认有16个数据库,初始默认使用0…

最流行的视频创作者

说在前面 🎈不知道大家对于算法的学习是一个怎样的心态呢?为了面试还是因为兴趣?不管是出于什么原因,算法学习需要持续保持。 题目描述 给你两个字符串数组 creators 和 ids ,和一个整数数组 views ,所有数…

C语言之实现贪吃蛇小游戏篇

text.c #define _CRT_SECURE_NO_WARNINGS 1 #include "snake.h" void test() {int ch 0;do{Snake snake { 0 };//创建了贪吃蛇//1. 游戏开始 - 初始化游戏GameStart(&snake);//2. 游戏运行 - 游戏的正常运行过程GameRun(&snake);//3. 游戏结束 - 游戏善后…

2023-12-15 Fri

坚持打卡第47天 健康方面(非常重要‼️) 每天运动,有氧和拉伸为主,运动十几分钟即可✅(1h)零点之前睡觉早上八点之前起床✅ 学习方面 力扣每日一题✅每天把当天APP上的单词复习完,并复习至少…

java导出word使用模版与自定义联合出击解决复杂表格!

1. 看一下需要导出什么样子的表格 如图所示,这里的所有数据行都是动态的,需要根据查询出来的数据循环展示。 如果只是这样的话,使用freemarker应该都可以搞定,但是他一列中内容相同的单元格,需要合并。 这对于表格样式…

conda命令克隆(复制)环境

前情介绍 最近有个需求是:根据已有的环境生成一个新的环境,也就是所需的新环境有大多数包和已有的环境都是相同的,需要改的只是部分,所以呢,克隆一个就再适合不过了! 所需命令 conda create -n B --clone…

java设计模式-工厂方法模式

1.工厂方法(FactoryMethod)模式的定义 定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。 2.工厂方法模式的主要优缺点 优点: 用户只需要知道具体工厂的名称…

HPM6750系列--第九篇 GPIO详解(基本操作)

一、目的 在之前的博文中我们主要介绍了不同系统不同开发编译调试环境的配置和操作(命令行方式、Visual Studio Code、Segger Embedded Studio for RISC-V),以帮助大家准备好学习环境为目的,但是未涉及到芯片本身以及外设的讲解。…

【linux】图形界面Debian的root用户登陆

图形界面Debian默认不允许以root用户登录。这是出于安全考虑,以防止用户使用root权限执行可能损害系统的操作。 如果需要使用root用户,可以通过以下步骤进行登录: 打开终端,使用su命令切换到root用户。修改/etc/gdm3/daemon.con…

大型网站架构演进过程

架构演进 大型网站的技术挑战主要来自于庞大的用户,高并发的访问和海量的数据,任何简单的业务一旦需要处理数以P计的数据和面对数以亿计的用户,问题就会变得很棘手。大型网站架构主要就是解决这类问题。 架构选型是根据当前业务需要来的&…

SpringMVC 返回 html 视图页面,SpringMVC与Servlet,Servlet重定向与转发

1. SpringMVC与Servlet的关系 SpringMVC框架是建立在Servlet之上的,提供各种功能,各种封装,各种方便的同时,它一点儿也没有限制Servlet,我们完全可以在SpringMVC的controller中,完全按照Servlet的方式来写…

会话技术+请求转发+重定向

会话包括有状态会话和无状态会话。 有状态会话是指在网络通信过程中,服务器需要记住客户端的状态信息,以便后续的通信能够继续使用这些信息。 无状态会话是无法持续保存信息,http超文本传输协议中的http属于无状态会话 如何保存会话&#xff…

时序预测 | Python实现XGBoost电力需求预测

时序预测 | Python实现XGBoost电力需求预测 目录 时序预测 | Python实现XGBoost电力需求预测预测效果基本描述程序设计参考资料预测效果 基本描述 该数据集因其每小时的用电量数据以及 TSO 对消耗和定价的相应预测而值得注意,从而可以将预期预测与当前最先进的行业预测进行比较…

第二十五章 STL- 常用算法

概述: 算法主要是由头文件<algorithm> <functional> <numeric>组成。 <algorithm>是所有STL头文件中最大的一个&#xff0c;范围涉及到比较、 交换、查找、遍历操作、复制、修改等等 <numeric>体积很小&#xff0c;只包括几个在序列上面进行简…

JS中的String常用的实例方法

splice():分隔符 把字符串以分隔符的形式拆分为数组 const str pink,red;const arr str.split(,);console.log(arr);//Array[0,"pink";1:"red"]const str1 2022-4-8;const arr1 str1.split(-);console.log(arr1);//Array[0,"2022";1:"…

Vue3快速上手笔记

Vue3快速上手 1.Vue3简介 2020年9月18日&#xff0c;Vue.js发布3.0版本&#xff0c;代号&#xff1a;One Piece&#xff08;海贼王&#xff09;耗时2年多、2600次提交、30个RFC、600次PR、99位贡献者github上的tags地址&#xff1a;https://github.com/vuejs/vue-next/release…

实操Nginx(七层代理)+Tomcat多实例部署,实现负载均衡和动静分离

目录 Tomcat多实例部署&#xff08;192.168.17.27&#xff09; 1.安装jdk&#xff0c;设置jdk的环境变量 2.安装tomcat在一台已经部署了tomcat的机器上复制tomcat的配置文件取名tomcat1 ​编辑 编辑配置文件更改端口号&#xff0c;将端口号改为8081 启动 tomcat&#xff…

SpringBoot之响应的详细解析

2. 响应 前面我们学习过HTTL协议的交互方式&#xff1a;请求响应模式&#xff08;有请求就有响应&#xff09; 那么Controller程序呢&#xff0c;除了接收请求外&#xff0c;还可以进行响应。 2.1 ResponseBody 在我们前面所编写的controller方法中&#xff0c;都已经设置了…