摘要:导读随着数据湖技术的普及,异构数据治理成为企业面临的核心挑战。小米数据平台集成 #Iceberg、#Paimon、#Fileset 等格式,管理 10 万+表格和百 PB 数据,面临元数据分散、治理复杂等难题。本文将分享小米 DataLake Governan
导读随着数据湖技术的普及,异构数据治理成为企业面临的核心挑战。小米数据平台集成 #Iceberg、#Paimon、#Fileset 等格式,管理 10 万+表格和百 PB 数据,面临元数据分散、治理复杂等难题。本文将分享小米 DataLake Governance 实践,详解统一元数据服务、智能优化策略等创新方案,探索高效治理之道。
本次分享会围绕下面四个主题展开:
1. 多湖表格式治理挑战
2. 一体化智能治理实践
3. 总结与展望
4. 问答环节
分享嘉宾|包锴楠 小米 软件研发工程师
编辑整理|孟立诗
内容校对|李瑶
出品社区|DataFun
01
多湖表格式治理挑战
1. 现状
小米#数据湖 平台目前已集成 Iceberg、Paimon、Fileset 等多种数据存储格式,以满足多样化的用数需求。随着 AI 技术发展,以及实时场景与离线场景的混合应用需求增长,平台逐步引入了多种数据湖格式支持。
AI 场景: 目前主要采用 Fileset 存储非结构化数据,AI 场景中存在大量此类数据。通过 Fileset 实现统一管理与审计。同时,针对部分 AI 场景中的结构化数据,会将其写入 Paimon 或者 Iceberg 表。实时场景:Paimon 对实时场景的支持比较友好,因此多数实时场景下的作业已接入 Paimon 表进行处理。离线场景:基于 Iceberg 表成熟的生态和稳定性,大部分离线作业、离线场景仍采用 Spark 与 Iceberg 相结合的模式运行。当前平台共管理约 10 万张表格,百 PB 数据,治理体系中每天会处理近 40 万条优化任务,以保持表的健康运行。
2. 挑战
随着数据规模的不断增加和图表格式的日益多样化,现有治理体系面临诸多挑战。
元数据访问接口不统一:当前湖表格式的元数据访问方式存在差异。例如,Iceberg 的元数据通过 Hive Metastore 访问,而 Paimon 的元数据存储在 HDFS 上,缺乏统一的访问接口。各类湖表格式的元数据访问方式各不相同,为治理工作的第一步——元数据采集带来了困难。治理需求更加多样化:随着数据格式的增加,每种格式都有特定的治理需求,导致整体治理需求不断增长,这对智能识别和治理能力提出了更高要求。治理链路不断扩展:不同数据湖格式需要构建不同的治理链路,可能涉及多个治理服务。随着服务的增多,需要搭建多个独立运行的服务体系,使得运维复杂度不断升高。基于上述挑战,我们急需建立统一的治理与调度服务体系。在这种背景下,平台正式引入了一体化智能治理服务——DataLake Governance。
02
一体化智能治理实践
1. 治理体系全貌
首先来看一下小米的治理体系全貌:
湖表格式:支持多种湖表格式,包括 Iceberg、Paimon 和 Fileset 等主流存储格式。元数据访问:Governance 服务通过 Gravitino 提供的统一元数据接口访问各类湖表数据,并基于元数据分析智能生成优化任务。任务产生与执行:最后将生成的优化任务分发到具体的组件执行,例如 通过 Spark procedure 执行。对于特殊场景任务(如 Fileset 数据上云迁移),当任务类型不支持 Spark 执行时,系统将通过 MapReduce 任务实现数据搬迁处理。整体架构支持多种任务类型的统一调度与执行。
2. Gravitino:统一元数据访问接口
随着需要支持的数据格式越来越多,每种格式都会有一些之前独立访问元数据访问接口,导致治理系统越来越臃肿。由于系统依赖多个服务组件,一旦某个服务出现故障,就会影响整体的稳定性,这显著增加了运维压力,需要长期监控众多服务运行的健康状态。
为此,我们引入了统一的元数据访问服务 Gravitino。
Gravitino 作为一种高性能元数据库,其主要优势如下:
统一元数据访问的 API:所有湖表元数据都可以通过 Gravitino 直接访问。统一的访问控制:统一的鉴权控制和审计,极为便利。屏蔽了底层存储的差异:仅需与 Gravitino 进行交互即可,无需再关注底层元数据存储。在引入 Gravitino 之后,小米的代码治理服务得到了极大的简化。
3. 智能治理:提供多样化治理能力
接下来介绍我们在智能治理方面的实践。
(1)Iceberg 治理需求
针对 Iceberg 的多层文件系统,每一层级都需要进行治理,从而保证表的健康状态。例如:
Iceberg 的快照文件,需要定期清理,特别是在流式场景下,会持续写入快照,如果不及时清理,整个表就会非常臃肿。针对元数据文件 Manifests,需要定期执行重写、聚合元数据。重写可以加速 Iceberg 表的查询 plan 的过程,避免元数据散乱,致使 plan 过程缓慢。文件层的治理,需要进行小文件合并以及 Z-Order 聚类等优化措施来加速查询过程。因为有些流式作业,通常采用 Merge On Read 模式,有时会写入一些 DELETE 文件,如果不及时合并,查询效率就会下降很多。最后,需要统一执行冗余文件清理操作。作业失败提交会产生许多冗余文件,快照清理过程中也可能出现清理失败的情况,这些都需要通过治理机制进行有效处理。①Iceberg 治理需求-文件治理
文件治理包括小文件合并与 Z-Order 聚类两大部分。
其中 Z-Order 聚类,因需要进行排序,所以资源消耗较大,一般是小文件合并的数倍。为了避免全量进行区域扫描,导致每一张表都进行 Z-Order 的聚类,而产生资源压力,我们制定了选择性触发规则,仅针对特定一小部分表实现 Z-Order 聚类。
例如我们会依据用户的查询模式,筛选出热点表,在表中用户每次查询时增加一些条件语句,收集这些查询条件语句,进而统计出热点字段。
在完成全表分区计算后,系统会统计每个分区的增量数据条数及热点字段的 NDV(Number of Distinct Values,唯一值个数)。筛选出 NDV/数据条数大于某个阀值的字段,以此为索引执行 Z-Order 聚类。该方案能有效过滤一些不需要进行 Z-Order 聚类的表,在保障治理效果的同时,减少计算资源消耗。
②Iceberg 治理需求-分区信息展示
我们在实践中发现,用户使用 Iceberg 的过程中经常有查看分区的需求,有时定期地、轮询地去查看某个分区,特别是针对流式写入的表,需要定期查看这个分区写得怎样、数据有多少等。然而,与 Hive 不同,Iceberg 本身不支持直接展示分区信息,每次查询都需要执行元数据的聚合计算,这对于小表还可以接受,但是对于大表来说计算压力是非常大的。
在初始架构中,我们尝试在元数据服务层进行分析计算,但是实际应用中发现这样做会为元数据服务带来巨大压力。为此,我们将 Iceberg 源码中添加了分区计算的任务机制:
当一个表更新时,将此事件投递到治理服务中。触发分区计算任务,由 Spark 执行。Spark 执行完成后,将计算后的分区信息写入到 Iceberg 的 Meta data 中(即 Iceberg 的元数据文件)。通过该优化方案,当用户需要查询表的分析信息时,可直接从已经被记录的文件中直接读取当前表的所有分区信息,不需要去重复执行计算。即使面对多次查询请求,系统也只需要读取同一份数据即可响应。这种改造显著降低了元数据服务的压力,同时提升了用户使用体验(即每次读取时都能很快返回此分区的结果,做到快速响应。)
③Iceberg 治理需求-数据生命周期管理
生命周期管理是一个非常普遍的需求,不管是 Iceberg、Paimon 和 Fileset 中,都会有生命周期管理的需求,我们将数据分为热数据、冷数据和过期数据三层分层存储策略,从而降低总体存储成本:
热数据:例如用户刚刚写入的近期访问数据,我们认为对读写的速度要求比较高,也会经常去访问它,所以存储在 HDFS 中。冷数据:近期访问少或无访问的数据,我们会将它异步搬迁到公有云上,降低存储成本。过期数据:超过一定期限的数据,会进行自动的 TTL 清理。我们首先获取用户指定的生命周期校验字段和用户表的分区信息(例如某分区记录为:date=20250326),然后将该分区时间与当前时间进行比较。
如果它小于用户设置的周期, Governance 服务将生成数据过期任务直接投递给 Spark,让其执行分区信息数据的删除;如果此数据处于冷数据的范围内,则触发上云转储流程。上云转储是我们在源码中修改的一个任务,本质上是文件重写任务,只是重写的介质不是 HDFS,而是改为云上的一个地址。基于上述过程,即实现了数据生命周期的管理。
(2)Paimon 治理需求
Paimon 原生就支持很多异步的清理过程,如 Paimon 在流式作业中会自动清理过期快照,并自动进行文件合并。因此我们在实践中暂时不将这部分纳入治理服务。
但是有一部分内容,如孤儿文件清理,我们会将它纳入到治理服务。此外对于过期数据清理,本身 Paimon 具备相应能力,但是如果直接执行达不到审计的目的。为了满足审计要求,我们也将这部分内容纳入到了我们的治理服务当中。
另外,与 Iceberg 类似,Paimon 同样有数据分层的需求,因此,我们正在逐步完善 Paimon 异步转储上云的过程,会将一部分代码提交到社区进行共建。
(3)Fileset 治理需求
Fileset 作为 Gravitino 中专门针对非结构化数据管理设计的资源形式,提供了 AI 和大数据场景下非结构化数据的统一读写、生命周期管理、资产管理和审计等能力。
对于 Fileset,目前我们也支持了过期数据的清理和冷数据上云来降低成本。同样也是通过 Governance 服务来完成这一过程。
4. 统一调度体系
最后来介绍一下小米统一调度体系。
小米的调度体系支持多种优化任务类型,这些任务类型通过统一调度机制分发到各执行组件,例如 Spark、Hadoop 客户端等,以实现优化任务的具体执行。
在早期版本中,我们只有 Iceberg 一个优化服务。随着湖表格式的不断扩展,服务也随之增加,例如引入 Paimon 时为其创建了一个独立优化链路,引入 Fileset 则为其又创建了一个独立优化链路。同时内部有很多分集群的表,各个集群之间的数据是隔离的,就要为各个集群再创建一些优化任务,所以总的部署服务量=集群数*湖表种类个数*单个湖表的链路服务数量,这样在中后期会越来越庞大,因此为运维和监控带来巨大压力。为此,我们认为构建统一的任务调度体系是必要的。
下面将从生产、调度和执行三个层级来介绍整套调度体系。
(1)任务产生
在实践中,我们将任务分为周期性任务和触发式任务:
周期性任务:主要处理两类场景,一类是执行频率较高的任务,例如小文件合并,大概每 10 分钟就会检测一轮是否有文件需要合并;另一类是逻辑判断比较简单的任务,例如快照清理,仅需要执行其自身逻辑即可。这些任务会放到周期性任务体系中,但不同任务类型调度周期可能会不一样。触发式任务:通过接受外部投递的消息触发(如分区信息计算任务,不需要周期性触发)。我们在实践中发现部分表可能每周更新一次,如果采用每天轮询的方式会造成资源浪费。因此,我们设计了基于快照更新的触发机制:当表的快照更新时,系统会投递相应事件,从而触发相关任务执行。还有一类判定规则比较复杂的任务,如 Z-Order 聚类,这类任务需要收集用户查询特征并进行判断。如果逻辑比较复杂的任务大量集中在服务本体中,会导致系统臃肿,所以我们将这些复杂计算放在外面进行统一管理。
下面分别介绍周期任务和触发式任务遇到的一些挑战和解决方案。
在早期版本中,表的数量不多(约 1000 余张表),因此我们使用定时的线程一次性处理所有表格。由于每轮处理的时间非常短,远小于调度定时间隔,此时架构是能够满足需求的。
随着规模逐渐增大,现在已超过 10 万张表,如果一轮一轮处理表,可能会超过定时间隔。这样会造成优化不及时,例如一些实时任务需要 Iceberg 及时 compaction,否则查询就会很慢,影响到下游任务。但是为每个表定时建立一个线程也是不现实的。
综合分析后,我们提出一种折中方案,即分桶模型。
对于一张表来说会有很多任务,我们将许多表的同类任务(如 100 张表的相同类型任务)聚合放到一个任务桶中,通过 Quartz 调度框架进行定时调度执行。
例如:有 10000 张表,拆成 100 个桶,采用分时调度机制,使得每一个桶在不同时间段独立执行调度,这样每一次调度只需要处理 100 张表即可。
这种分桶模型的设计有效分散了每个任务需要扫描表的数量,避免了单次扫描中引入太多表而导致的处理延迟。依靠这个优化模型,我们成功实现了对上万张表周期性调度的支持。
触发式任务设计目的是将一个复杂的状态外置,比如说 Z-Order 的聚类判定、数据转储、数据转发以及分区计算等场景。我们采用一个监控服务实现:如果有触发任务时,会投递到监控服务后生成触发任务,并与周期任务纳入统一调度。这样的设计增加了任务的灵活性,发现表需要进行某种优化任务,可以先快速构建外部服务来进行任务触发并投递到调度体系中,不会影响本体的代码流程。
(2)任务调度
任务调度也分周期性任务和触发式任务来分别处理。
在任务生成后,调度层将根据任务类型将其分发至相应组件执行,过程中需要与多种组件进行交互,例如 Spark Job、Map Reduce 任务,以及可以直接操作 HDFS 客户端(如清理 Filter 过期任务)。由于不同组件的交互时间和执行时长存在显著差异,为避免任务间相互阻塞,我们构建了基于状态机的任务调度机制。
我们将任务分为四种状态,即 created、submitting、running 和 finished。
以 Iceberg 任务为例,其生命周期始于 created 状态。Changer 类会首先检查当前是否存在同类任务的重复执行实例,若不存在则将该任务状态流转至下一个状态。在状态流转过程中,系统持续轮询获取待处理任务,当识别到 Iceberg task 时,会将其分发至对应的 Iceberg Processor 进行处理。Processor 根据任务类型进行判断,若确认为 Spark 任务,则将其投递至 Spark 组件执行。
通过这个流程,我们将整体复杂的逻辑链路拆解成多个不同的部分。简化了代码编写的难度,并且任务之间不会互相阻塞,因为这里每个状态都是会在数据库中被持久化的,所以就算某个任务执行很慢也无需等待这个任务,继续执行其他任务即可。
(3)任务执行
下面介绍任务执行过程中的一些优化。
之前 Iceberg、Paimon 源码只支持分布式调度,而在实践中,对小表操作(如 Paimon 的 open clean 任务)采用统一的分布式调度策略是没有必要的。无论大表还是小表,均采用分布式调度到 Executor 节点执行,这在实际场景中并不合理。
因此,我们对 Iceberg、Paimon 的源码进行了增强改造,修改为支持 Local 和分布式两种模式去执行优化任务。
小表(数据规模小的表):只调度到一个 Driver 节点多线程执行,因为有时分发调度的时间会远远大于执行时间,有些执行任务本身只要毫秒级或者秒级就能执行完,如果分发到下面节点去执行可能需要 10 多秒。而且分发到其他节点会占用额外的 CPU 的资源。通过这一改造,优化了对 CPU 的资源需求,从整体上增加了任务吞吐量。大表(数据规模大的表):对于大表单点执行是不现实的,文件特别大可能会造成单点 OOM 的问题,所以只能利用分布式能力去执行,以加快执行速度。03
总结与展望
最后,回顾一下本次分享的内容。随着 Iceberg、Paimon 和 Fileset 等多种数据存储格式的引入,业务的多样化用数需求得以满足,但同时也带来了治理上的挑战。为降低运维成本并提升治理效率,我们构建了统一的智能治理体系。基于这一体系,目前已稳定支撑 10 万+表格、百 PB 数据的治理。
未来,主要工作规划如下:
AI 数据管理:引入更多湖表结构来支持 AI 数据管理。例如我们目前正在调研 Lance 湖表结构,后期待其技术逐渐成熟后,将考虑引入到统一治理体系中。元数据湖建设:继续推动元数据架构的统一化进程。由于 Iceberg 使用历史较长,存在部分遗留架构包袱,因此需要逐步完成向 Gravitino 统一元数据架构的迁移,该过程将分阶段实施。治理体系升级:持续升级治理体系,以支撑更大规模的数据资产管理和更多样化的优化目标实现。04
问答环节
Q1:前面介绍的治理方案,如 Iceberg 的治理、冷热生命周期的治理,如果底层已经用对象存储,那么元数据的工作还有效吗?考虑到对象存储本身已经提供文件级别的自动变冷功能。
A1:我们内部已构建了对象存储自动变冷的架构,但目前仍处于实验阶段。
若未来能在对象存储实现自动变冷,且经测试速度能够达到我们的要求,那么可考虑取消数据冷热分层流程。但数据过期生命周期管理仍需保留。但当前技术演进仍需较长时间,目前还无法完全脱离 HDFS 架构,因此现阶段冷热分层机制仍有必要。若未来对象存储性能更强大,则可抛弃数据分层这一部分。
Q2:从热数据到冷数据的转变过程,小米这边是否是通过后台的异步 Spark 任务实现数据的搬迁以及元数据的更新?
A2:是的,这是一个异步重写的过程。
Q3:对于 Fileset 数据的去冷,小米也是采用同样的流程?
A3:Fileset 目前不支持用 Spark 任务去执行,所以我们主要采用 Hadoop 本身的能力,通过定期执行 Map reduce 任务实现数据搬迁。
以上就是本次分享的内容,谢谢大家。
来源:DataFunTalk