集团数据湖表优化与管理实践

B站影视 韩国电影 2025-03-10 09:00 2

摘要:抖音集团内部使用的数据湖格式称为 ByteLake,它是基于 Apache Hudi 深度定制的一个数据库框架,其核心功能包括支持 ACID、增量消费更新和湖仓统一元数据管理,适用于数仓分析、交互式分析及特征工程等场景。

导读本文将分享抖音集团数据湖表优化与管理实践。

主要是从四个部分进行介绍:

1. 抖音集团数据湖表的实现原理

2. 数据湖表管理的问题与挑战

3. 抖音集团数据湖表管理服务

4. 与 Amoro 的融合及社区贡献

分享嘉宾|张永翔 杭州抖音科技有限公司 数据湖存储专家

编辑整理|程思琪

内容校对|李瑶

出品社区|DataFun

01

抖音集团数据湖表的实现原理

抖音集团内部使用的数据湖格式称为 ByteLake,它是基于 Apache Hudi 深度定制的一个数据库框架,其核心功能包括支持 ACID、增量消费更新和湖仓统一元数据管理,适用于数仓分析、交互式分析及特征工程等场景。

1. 核心概念

ByteLake 的核心概念是 timeline,该概念继承自 Hudi timeline 概念,其核心是维护不同时刻在表上的操作(Action)集合。每个 timeline 上的操作由三个部分构成,包括 action 的类型(如 ReplaceCommit、DeltaCommit、Compaction、Clean 等)、时间戳(不同于 Apache Iceburg 的 Snapshot,ByteLake 的 timeline 记录每个操作开始的时间而非提交时间)以及状态(通过状态机管理,经历 request, inflight 和 completed 的过程)。

在服务层,抖音的 ByteLake 提供了两个元数据管理服务:ByteLake Metastore (BMS) 和 Global Catalog service。前者提供数据湖表元数据管理能力,类似于 Hive Meta store,负责 timeline 和 Snapshot 访问接口,并实现了数据湖标数据访问的并发控制能力,如读时心跳、任务租约和提交时冲突检测。底层采用了可插拔的元数据存储层,支持 HDFS 或 MySQL 元数据存储,通常使用分布式 MySQL 存储集群实现 ByteLake 表的元数据存储。

在 ByteLake Metastore 之上,我们还实现了 Global Catalog Service,对外提供统一的元数据视图。它完全兼容 HMS API 接口,在对外的元数据暴露层面,它与 Hive 的数仓表实现了完全兼容。此外 Global Catalog Service 实现了不同数据中心间的元数据访问路由,并且在底层支持 HMS 和 ByteLake Metastore 之间进行横向扩展,同时提供分区级别的路由映射,将 Hive 表部分分区路由映射成 ByteLake 表,便于 Hive 表和 ByteLake 表的存储格式转换迁移。

2. ByteLake 对 Hudi 的优化改造

开源的 Apache Hudi,文件 layout 大致如上图所示。在表下面有一个 .hoodie 的元数据目录,里边组织了所有的 timeline。每一个 action 在元数据层面都以文件的形式去存储,它的 request、commit 和 inflight 状态分别由三个不同的文件存储,然后通过 HDFS 层面的 CIS 操作来保证 timeline 操作的原子性。在这一层面我们完全是由 ByteLake Metastore 服务完成托管。

在分区下数据文件的组织方面,Hudi 是通过 filegroup 的方式进行组织,每个分区下所有的文件会分布成不同的 filegroup,内部拆分成不同的 file slice,每个 file slice 其实就是这一组文件的一个版本。每个 file slice 包括 parquet 文件和 delta 文件,分别作为 base 文件和增量数据文件。读取的时候需要做 parquet 文件和 delta 文件的合并操作。在 ByteLake 中抛弃了 Hudi 的 file group 和 file slice 的概念,改为直接按照 bucket 对文件进行组织,并以 append 方式直接生成新文件,提高了流式上传效率并减少了写入延迟。

ByteLake 的读写过程进行了改造。开源 Hudi 的 Upsert 过程中,首先需对 input 数据进行打标,它需要通过 index 的索引去寻找所有每个 input 的记录属于哪一个 Filegroup。如果 tag 不存在,就说明没有一个已经存在的 Filegroup 去映射这个 record,它就会产生一个新的 Filegroup,并产生一个 base 文件;如果 tag 是存在的,就会写入一个已有的一个 Filegroup,生成 delta 文件。

ByteLake 在这方面取消了 tag 的步骤,将所有的 input 数据按主键去进行 shuffle,然后去计算它的 bucket,我们这边要求 bucket 一定要包含组件 append,然后将下发后的数据直接 append 写入 parquet 文件,这样相比社区版 Hudi,写入过程取消了 tagged 步骤,对写入的 QPS 实现了非常大的提升,对 StreamUpsert 更加友好。

在读取过程方面,ByteLake 将去重的逻辑提升到了引擎层面。Apache Hudi 中 MergeOnRead 的过程在 HoodleMergeOnReadRDD 里面完成。读取的过程首先会构建 read split,并按照 fileslice 的 group ID 去做拆分。对于一个 fileslice 内部,首先会构建一个 split Hashmap,进一步读取 file slice 下面所有的 log 文件进行去重,再读取 parquet 文件去重,最后输出去重后的组件数据,交给后续的算子进行处理。

而 ByteLake 读取时不再进行算子内部去重,而是利用 Windows 的算子进行去重。具体而言,在构建 ReadSplit 的时候,不去根据 file group ID 来拆分,而是直接根据 bucket 进行拆分,使用 Spark 的 ParquetScanRDD 进行读取,然后修改 Spark 的执行计划,去构建一个 Windows 算子,通过 partition key 和 primary key 进行 group by,再根据我们的预聚合 UDF 进行排序。因为排序前已经使用 bucket 进行了拆分,所以此次排序仅需进行 local sort,在性能方面表现很好。最后进行排序取 TOP1 的操作,从而实现组件表的去重逻辑。

02

抖音集团数据湖表管理的问题与挑战

由于 ByteLake 在 Hudi 的基础上做了非常大的改动,所以后续对于 ByteLake 表的优化方式与原生 Hudi 也有所不同。

Apache Hudi 采用了 Inline 的操作执行模式,即在数次 delta write 后触发 Compaction 操作。有两种执行模式,一种是同步模式,一种是异步模式。

在同步模式下,Hudi 会阻断下一次的 Deltawrite 的写入,导致在 Streaming write 的场景下会出现反压,进而导致写入的 QPS 较低。

而 Inline 在异步执行模式下,会触发写入任务去调度 Compaction 操作,在此期间不阻塞写入任务,即 Compaction 任务与写入任务共享 Flink 资源。但是这种情况下,需要周期性调度 Compaction 任务,它会对任务本身资源产生竞争,导致稳定性或性能下降的问题。

我们前期尝试使用独立任务执行的实现路径解决上述问题,写入任务不触发文件合并任务,而是单独使用一个合并任务进行调度。这样的优势在于对写入任务的稳定性和性能都是最友好的。但是真实场景下我们发现这种方案的维护成本过高,每一个表都需要额外去维护任务,同时用户的理解成本也较高,此外因为每张表的上游在写入时所需的 checkpoint、bucket、文件数量等各不相同,任务调度的时机较难通过标准化的策略实现控制管理。

03

抖音集团数据湖表管理服务

为解决优化任务调度和执行中的痛点,抖音引入了全托管的表管理服务,该服务与 ByteLake Metastore 属于同一层级,负责 ByteLake 表优化任务的异步调度、提交、执行和监控全生命周期管理。它确保了 ByteLake 表的写入性能和查询性能和稳定性,同时屏蔽了底层细节,使业务方能够专注于自身业务逻辑。

此外我们也基于表管理服务实现了 ByteLake 的一些高级功能,比如 TTL 能力的实现就是由表管理服务来进行托管。

表管理服务架构上采用了分布式设计,包含 API Server、Scheduler Service 和轻量级任务处理器 Light Process Worker 三个角色。

API Server 采用了一种无状态、多实例的部署方式,由 API Server 订阅 ByteLake metadata service 产生的 timeline 事件,生成表的优化任务,并存储到表管理服务的元数据库里。然后由表管理服务的 SchedulerServer 服务读取被调度出来的优化任务。SchedulerServer 采用 master/standby 架构,负责任务的提交、监控、告警、重试等职能,并负责资源的管理和配置。

第三个组件是 Light-process-worker 计算组件。通常集群将优化任务提交到 Yarn 或 Presto 集群上面执行。但轻量级的任务(如执行计划生成任务)往往直接部署在 Kubernetes 容器里面,这类节点即为 Light-process-worker 节点。

目前在抖音集团内部通过表管理服务支撑了全球 6 个数据中心的部署,共计托管了超过 1 万张 ByteLake 表,日均调度 50 万次优化任务。

04

与 Amoro 的融合及社区贡献

当前的数据湖表管理存在一些不足之处。

其一,目前 ByteLake 平台的设计目标是为了兼容 Hive 的元数据,在平台层面缺乏对 ByteLake 表元数据展示和服务的支持,包括 snapshot、 timeline 这些概念,目前没有办法在元数据平台上进行展示。

其二是运维工具的缺失,我们目前大量运维是依赖于脚本或者数据库操作,运维效率较低。此外,抖音集团面临合规化的问题,开发无法去访问非大陆区域以外的生产数据,这使得我们去倒逼实现一个标准化、规范化的运维工具。

我们目前的想法是基于 Amoro 来实现运维工具的规范化。我们目前尝试了对 ByteLake 表管理服务和 model 服务进行融合,使用 Amoro 进行表管理服务的展示。

下一步我们预想基于 Amoro 进一步补齐当前的短板,通过开发基于 Amoro 的标准化开发运维工具,改善用户体验,并符合海外合规环境下的运维要求,进一步探索基于表管理服务为数据湖表构建二级索引。

在 Amoro 社区方面,抖音将加强与 Amoro 社区的合作,共同推进开放数据服务表管理平台的发展。

当前 Amoro 的定位是作为一个开放的数据服务表管理的平台,但是目前提供的网络服务还是主要局限在 Iceburg 的领域,未来我们希望通过深化 Amoro 和 ByteLake 表元数据管理服务的融合,扩展 Amoro 对开放数据湖表格式的支持能力。

此外,我们会将目前表管理服务的这种部署的架构推向社区,实现分布式的横向扩展部署的能力,从而进一步推动 Amoro 的分布式部署,以便更好地适应大规模生产环境。

目前 Amoro 的 process 只能实现 Iceburg 表的优化。未来我们会抽象出更加通用的、更加泛化的 process 接口,使它可以实现更加复杂的表管理任务,如数据搬迁等。这不仅有助于增强 Amoral 的功能,也为社区带来了新的发展方向和技术积累。

以上就是本次分享的内容,谢谢大家。

来源:DataFunTalk

相关推荐