vivo 离线业务现状及湖仓组件选型
1.1 离线业务现状与痛点
在 vivo 的离线业务场景中,我们的数据处理流程与大多数企业的数据仓库或传统湖仓架构类似。我们的数据集成环节涵盖了多种数据源,如埋点日志、服务日志以及 TP 数据库等。这些数据进入到企业级数据仓库后,会采用经典的数据建模方法,比如雪花模型,构建从 ODS 层到 DW 层,再到 DA 层的数据处理链条。最终,这些数据支持着包括报表分析、算法推荐及数据服务在内的多种应用。
在上述过程中,普遍存在几个痛点:
数据更新成本高:对于实时表中的延迟数据更新需求,特别是需要回溯至数周甚至一个月前的情况,由于缺乏有效的增量更新机制,导致必须执行全量重写操作,这不仅增加了计算资源消耗,还延长了处理时间。
不支持行级删除:在企业数据管理中,行级删除是一个常见的业务痛点,尤其是在处理敏感或合规性要求较高的数据时。这类数据不仅需要从逻辑层面进行移除,更需确保其物理存储上的彻底清除。在基于 Hive 与 Spark 构建的数据仓库架构下,实现这一目标类似于更新操作,往往不得不采取全表重写的方式,这也极大地增加了计算资源消耗及时间成本。
查询提速受限:鉴于 Hive 本身并不支持索引或其他提速技术,很多情况下,即便是普通的点查或者分区批查,也可能被迫进行全分区或全表扫描,导致用户查询受限。虽然可以通过外置索引或者冷热数据分层进行提速,但这往往会导致存储翻倍,这对于用户来说并不是理想方案。
数据产出时效低:基于 Spark + Hive 的传统批处理模式,数据产出延迟通常是天级别的,即便优化后也只能达到小时级别。面对海量数据增长以及对数据产出时效具有强诉求的业务,这种延迟已经成为制约整个大数据平台效能的关键因素之一。
1.2 湖仓组件选型
vivo 湖仓架构的组件选型采用了相对标准的方案。在计算引擎层面,针对离线任务选择了 Apache Spark;对于实时流处理需求,则采用了 Apache Flink;此外, OLAP 分析引擎选用了 StarRocks。统一表格式采用覆盖度较高的 Paimon。数据格式方面,鉴于历史原因,使用的是 Parquet、ORC 格式。
1.3 离线湖仓架构升级
vivo 互联网业务,每日增量数据规模为 PB 级别,可能从几十到几百 PB 不等,入湖的数据条数通常达到万亿级别,数据采集层到 ODS 层的负载非常大。在离线数仓架构升级前,数据处理延迟大部分场景下是天级延迟;针对一些对时效性有较高需求的应用场景,则优化到了小时级。随着离线数仓架构升级,采用 Spark + Paimon 组合架构后,离线业务整体数据处理延迟缩短至分钟级别。接下来,我将详细介绍我们在增量计算降本增效方面,多个重要场景下的具体实践。
02
增量计算增效降本实践
2.1 归因/拉链表等场景优化
在我们的归因场景中,需要定期更新多个数据分区。以用户行为数据上报为例,假设一个用户在一周前下单,但一周后可能需要进行退货或其他操作。这种情况下,迟到的数据需要我们回溯并更新 N 天前的相应分区。传统的方法是采用 Spark 与 Hive 结合的方式进行全量与增量数据的 full join,再基于此结果执行进一步的逻辑处理,并最终将处理后的数据 Insert Write 到相应的 Hive 分区中。这种方法虽然直接但存在显著缺陷:首先,它导致了极高的计算资源消耗;其次,整个过程耗时较长;最后,对于用户而言,编写和维护此类 SQL 语句也是一项繁琐的任务。
当我们升级到基于 Spark + Paimon 构建的湖仓架构后,就解放了很多生产力。我们无需再关注增量数据与全量数据之间的逻辑关系。只需在 Paimon 表层面构建一个数据模型,该模型能够对标类似宽表或 MySQL 的建模逻辑。具体而言,针对需要更新的数据表,我们依据主键字段以及需要排序的 Sequence Files 来定义其结构,然后将将增量数据 INSERT INTO 至 Paimon 表内,从而实现数据更新。根据实际测试,在相同任务条件下,相较于传统的 Spark+Hive 方案,新的湖仓架构能够使 YARN 集群上的内存时消耗下降80%,原本可能需要耗时几十分钟才能完成的数据更新工作,在采用 Paimon+Spark 组合后仅需几分钟即可迅速完成。
湖仓架构也适用于大部分拉链表场景。某些业务场景下需要记录用户的最早登录时间。通过调整 Sequence Files 的排序顺序,可以实现对拉链表的有效重构。将拉链表的部分维表切换到 Paimon 上,可以有效提升增量计算效率。在传统方法中,全量数据与增量数据均需参与 Shuffle 过程进行计算,这导致了较高的资源消耗。然而,在使用 Paimon 表结构时,对于数据更新操作仅需考虑增量部分即可。这种方式显著降低了计算成本,使得数据更新过程更加高效。
2.2 物理删除场景优化
物理删除场景与增量更新有相似之处。Paimon 作为一种先进的数据湖存储格式,支持高效的物理删除操作。如果使用 Spark+Hive 进行物理删除,通常需要通过一系列的 JOIN 操作来匹配目标数据,并将这些数据过滤掉重写。这种方法不仅耗时较长,而且对计算资源的需求较高,此外还需要用户编写复杂的 SQL 语句,降低了用户体验。相比之下,采用 Spark+Paimon 架构可以显著改善上述问题。Paimon 支持包括 Delete、Update 等多种语义,删除数据时,只需执行 Delete 语句加上过滤条件即可,在后续查询中不再可见,从而迅速满足业务需求。这些被标记为已删除的数据会在下一次合并过程中或者快照清理阶段彻底从磁盘上移除,实现真正的物理删除。这种机制使得行级删除变得更加高效且易于实现,可以将行级删除效率从分钟级提升到秒级,大大降低用户使用成本。除此之外,Paimon 还支持行级 TTL,允许设置数据记录自动过期的时间点。例如,在电商自动关单场景中,可以配置订单信息在创建24小时后自动删除。这一特性自 Paimon 0.8 版本起便已提供,极大地丰富了其应用场景,特别是在需要精细化管理数据生命周期的情况下。
2.3 数据拼接场景优化
涉及数据拼接场景,Paimon 同样展现出了强大的优势。在数据处理和特征工程领域,尤其是在构建复杂的数据模型时,通常需要整合来自多个数据源的信息。这些数据源往往共享一个共同的主键 ID,然后写入到一张表中。传统的方法是通过执行一系列的 JOIN 操作,通过大量 Shuffle,再将计算后的数据写入到对应分区,这通常导致计算时间和资源消耗显著增加。然而,采用 Paimon+ Spark 湖仓一体架构可以极大地优化这一流程。在这种架构下,我们创建一张宽表,将来自 N 个数据源但共享同一套主键的数据写入 Paimon 表,而无需担心其关联关系。这种方法消除了对复杂的 JOIN 操作的需求,因为数据在写入 Paimon 表的过程中就已经完成了自然的拼接。此外,在支持多分区的情况下,还可以利用并发写入机制进一步提高作业效率。下图是数据拼接场景比较通用的模型。Partial-Update 千亿级场景,更新时延可以从小时级直接提升到分钟级。
2.4 查询提速场景优化
在持续更新过程中,随着增量文件数量不断增加,查询性能不可避免地会受到影响。这是因为 Paimon 在执行查询时采用的是读时合并,这意味着增量越多,查询时合并的数据量越大,从而影响查询效率。针对这一问题,我们可以提供定制化的解决方案来满足不同用户的需求。对于那些主要关注写入性能而不太关心读取速度的应用场景,可以保持现有的数据管理策略。然而,如果用户的业务需求更侧重于高效的查询性能,我们则需要通过合并的方式提升查询效率。这种优化措施同时适用于主键表和非主键表。
此外,这种优化方法还能应对另一种常见的业务场景:同一张表在不同时期,针对不同字段有查询提速需求。例如,一张包含字段 A、B 和 C 的表,在上个月用户可能特别关注字段 A 的查询性能,但在最近一周内,用户的需求转变为同时加速字段 A 和 B 的查询速度。
在数据 Compaction 过程中,我们利用 Paimon 提供的 Compaction 能力,可以按照查询需求,选择不同的字段对表数据进行排序,就能够高效地调整字段进行查询提速,以适应各种不同的应用场景,还提供了丰富的合并算法,如 Z-Order 和 Hilbert 等,这些算法可以很好的帮助我们进行 Data-Skipping。具体来说,合并后的大部分点查场景,可以从分钟级提升至秒级。
除了 Paimon 自身优势,借助 StarRocks 的 MPP 架构及其卓越的数据摄取能力,部分点查场景甚至能达到毫秒级。此外,为了进一步提升用户体验,我们还增强了合并发数自动推断的能力,同时优化 StarRocks Paimon Catalog 支持 Cache,优化后大幅减少 getTable 时与 HMS 交互,极大提升了并发查询性能。最新版本 StarRocks 内置的 Paimon 版本比较低,不包含 Caching Catalog。如果是 StarRocks 的用户,可以将版本升级到0.9,或者 Pick Caching Catalog 补丁,如此可以大幅提升查询效率。应对大并发查场景时,如数百个并发请求同时访问规模达到数十亿乃至上百亿条记录的表时,查询效率将得到极大改善。举例来说,在未启用 Caching Catalog 的情况下,假设存在100个并发查询请求,每个请求可能需要花费十几秒才能完成;而一旦启用了 Caching Catalog,同样的查询任务可以在毫秒级别内快速响应。
03
微批入湖提速优化方案
在 vivo 的数据采集层,我们通过微批处理技术显著提升了数据入湖的时效性。在传统的数据采集场景中,大多数厂商或用户采用的模型相对一致,主要通过文件采集,并以实时或离线的方式,经过 ETL 加载至 Hive 中的 ODS 层。在 vivo 内部,离线场景每个小时会在数据源层面端滚动一次 HDFS 文件,利用 Spark 或其他工具进行 ETL 处理,然后直接搬迁到 ODS 层的 Hive 目录下。针对时效性要求比较高的场景,则使用 Kafka + Flink 进行数据采集,实时写入到 Hive 中,以提高 ODS 入湖时效。
上述两种场景都存在各自的局限性。当采用实时数据采集时,虽然实现了秒级的数据延迟,但代价是较高的资源消耗。具体而言,为了保持低延迟状态,必须持续运行 Kafka 集群及 Flink 作业,这导致了较低的资源利用率。另一方面,如果选择按小时批量处理模式,则面临的主要挑战在于较长的数据传输周期。特别是接近整点时,大量累积的数据需要在同一时间段内完成迁移与处理,往往会导致直到下一个小时开始后才能完成全部 ODS 层面的任务执行,进而影响后续数据分析工作的启动时间,造成约一小时的整体延迟。
基于上述两种情况,我们引入了微批采集方案。原先,我们的数据文件每小时滚动一次;现在,得益于湖仓一体能力,我们将这一时间间隔细化为每十分钟滚动一次,从而实现了更细粒度的数据分割与管理。Spark 每十分钟就会将增量数据写入 Paimon,每日零点就只需处理最后十分钟的增量,并将其离线写入至 Paimon 表中,即可完成整个 ODS 层的数据准备。这种方法时间延迟,由原来的1小时大幅减少到了几分钟内,极大提升了数据处理效率。ODS 层通常查询需求较低,用户只需执行单次读取操作。然而,当确实存在频繁访问需求时,则需要开启数据合并功能以确保性能。在此过程中,可能会有人提出疑问:为何不选择使用 Spark 结合 Hive 来实现类似的微批处理?实际上,这主要是由于 Hive 缺乏对主键的支持,无法有效支持增量更新模型。相比之下,Spark 与 Paimon 的组合能够更好地满足此类需求。相较于 Kafka+Flink,虽然 Kafka+Flink 可达到秒级响应速度,但 Spark+Paimon 的资源利用率至少高出一倍,同时仍能保证远超传统离线处理方式的数据时效性——即从之前的1小时缩短至不超过10分钟即可完成 ODS 层的数据初始化工作。这不仅加速了后续商业智能分析或关键业务逻辑的启动,也为企业带来了显著的成本效益提升。
04
海量历史数据迁移的解决方案
鉴于 Paimon 表的诸多优势,而许多用户现有的数据架构是基于 Hive 构建的。因此,如何将现有的 Hive 数据迁移到 Paimon 表,或者更广泛地说,如何平滑地切换成湖仓一体架构,成为了至关重要的议题。面对这一挑战,我们采用数据迁移和任务迁移两个策略。
4.1 数据迁移
在数据迁移方案中,我们区分了主键表(PK表)和非主键表(非PK表)。由于 Hive 表缺乏对主键的支持,因此主键表数据迁移场景相对较少。对于这类 PK 表,我们的策略是首先通过 INSERT INTO 语句建表,随后将数据导入 Paimon 表中。这一方法的应用场景较少,并不是迁移工作的核心重点。真正构成迁移挑战的核心在于非主键表的处理。针对非主键表,我们采取了一种更为高效且直接的方法:利用 Paimon 提供的迁移工具集来实现从 Hive 至 Paimon 的原地迁移。此过程中最关键的一点是整个迁移操作不涉及文件 I/O。具体而言,该方案直接将 Hive 文件拷贝至 Paimon 的数据目录下,之后基于现有 Schema, 构建 Manifest/Snapshot 元数据,再同步元数据至 HMS。这样就完成了 Hive 表到 Paimon 表的迁移,原始 Hive 表可以自行选择移除或保留。经过实践验证,利用上述方法可以极大地提高数据迁移效率。例如,在处理千亿级别表时,借助 Paimon 迁移工具,可以在几分钟内顺畅的完成数据迁移。此外,还可以在上层封装一键式或配置化的工具,就可以基于库或者多表,轻松完成迁移。
4.2 任务迁移
数据迁移之后,就会面临任务迁移难题。如何确保 Paimon 任务与非 Paimon 互不影响,是需要特别关注的问题。考虑到我们当前的 Spark 3.0+环境已经稳定运行多年,并承载了数千个任务,任何对现有环境的修改都必须谨慎处理以避免潜在风险。在团队内部沟通时,我们需要承诺在引入 Paimon 支持的同时,不会对历史上的任何 Spark 作业造成不利影响。
为达成上述目标,我们设计了一种方案来保证兼容性及稳定性。该方案主要从 Spark 侧进行实施。具体来说,当 Spark 引擎接收到 SQL 脚本后,首先会对其中的所有 SQL 语句进行全面解析,并与已有的元数据进行比对。如果识别出 SQL 查询中并不涉及 Paimon 表,则继续沿用原有的处理逻辑;反之,一旦检测到 Paimon 表,就会动态加载 Paimon JAR 包、切换为 Paimon Catalog,并将相应的规则注入到 Spark 中。通过这一系列步骤,原本普通的 Spark SQL 任务将被带上 Paimon 依赖,整个过程中既不会改变原有语法也不影响优化策略,从而实现了任务级别上的平滑迁移,同时也确保了对传统基于 Hive 的 Spark 作业无任何干扰。
05
Spark Procedure 兼容及增强
在 Apache Flink 生态系统中,Procedure 机制最初是为支持一系列高级功能而设计的,包括时间旅行、表管理和文件清理等。这一工具现已被集成到 Paimon 中,成为一种极其高效的管理手段,特别适用于以脚本形式或定制化程序形式维护线上 Paimon 表。
我们团队对 Paimon 中的 Procedure 进行了多项增强,首先是扩展了其元数据自定义修改的能力,强化了生命周期管理等功能。还针对特定场景如时间旅行进行了优化。考虑到许多用户是从 Hive 迁移到 Paimon 平台,他们往往希望避免因学习新的专有湖仓配置而带来额外负担。为此,我们对一些复杂概念如 Tag 和 Snapshot 进行了抽象封装,并引入了更为直观易懂的时间戳机制来代替。这样一来,当用户需要执行回滚操作时,只需指定具体的时间点即可完成任务,无需深入了解底层技术细节,降低用户使用成本。另外我们也补齐了 Spark Procedure 能力,与 Flink 的 Procedure 相互兼容,可以直接在 Paimon 的最新分支中使用。最后,我们还对部分单进程执行的 Procedure 进行了优化,使其具备多并发多进程执行能力的同时还增强了并发推断能力。
06
元数据监控方案
在我们的早期任务部署中,部分监控机制尚未完善,导致出现了一些潜在问题。例如,在某些实时写入的任务中,虽然启用了 Read-Only,但没有进行 Compact 等操作,导致元数据不断累积。随着时间推移,当用户尝试执行查询时,会发现响应时间显著增加。这种情况通常是在用户向平台报告后才被注意到,此时已经积累了大量的 Snapshots,并且没有及时进行清理或合并,这时候再进行数据治理就很晚了。因此我们需要加强监控体系,通过前置风险管理来预防上述问题的发生。
我们基于 Paimon 系统表构建了一套全面的监控体系。Paimon 系统表提供了丰富直接的元数据查询信息,用户可以通过 Spark 或 StarRocks 引擎直接读取 Paimon 系统表,并将获取到的信息高效地写入到 StarRocks 类表当中。这一过程中,我们针对系统表的查询下推进行了优化,显著减少了查询 I/O,提高了查询效率。
此外,我们在上游部署了监控告警服务,会根据用户预设规则(例如:当累积的元数据 Snapshot 数量超过阈值100时)自动触发告警机制,向相关责任人发送通知,以便他们能够迅速采取行动,无论是通过执行预先准备好的运维脚本还是请求平台介入处理,都可以构建 Paimon 元数据监控闭环,极大地增强了系统的可靠性和可维护性。
07
未来工作展望
当前 Paimon 的数据处理能力在 vivo 互联网大数据场景已经覆盖了大部分离线加速业务。未来我们会针对非结构化/半结构化数据查询、算法应用等场景进一步探索挖掘,同时会增强 Paimon 复杂类型查询能力、支持自定义索引、Merge Engine 等能力,为更多的业务场景赋能。