01#
导言
Alluxio 是一款开源分布式数据编排系统,它介于存储与计算之间,提供了分布式缓存、全局数据访问等能力,为跨集群大数据分析、AI 训练等场景提供数据加速服务。Alluxio 提供统一的客户端 API 和全局命名空间,使应用程序能够通过一个通用接口连接到多种存储系统,解决了数据访问延迟和存储系统兼容性的问题。
图 1 Alluxio 架构
在爱奇艺,我们主要利用 Alluxio 的分布式缓存能力,用于减少分散在多个 AZ (Availability Zone,可用区)的大数据集群跨 AZ 数据传输,节省专线带宽。同时,我们也将 Alluxio 用于解决数据分析场景下 OLAP 存储计算分离架构带来的数据查询延迟变大的问题,提升分析性能。
本文将介绍 Alluxio 缓存在这两个场景下的集成方案与管理策略,以及实践过程中遇到的问题和解决方法。
02#
Alluxio在大数据多AZ统一架构的应用
图 2 从多 AZ 演进为统一 AZ
在多 AZ 统一调度架构中,不同集群上的数据通过自研的 QBFS (iQIYI Bigdata FileSystem) 大数据文件系统进行统一路由。上层 Spark、Flink 等大数据计算框架统一对接 QBFS,无需关心数据在哪个集群上,这引发大量的跨 AZ 数据访问,带来跨 AZ 传输延迟、性能波动及专线网络流量等新挑战。为此,我们引入 Alluxio,与 QBFS 大数据文件系统集成,构建了跨 AZ 的 QBFS-Alluxio 缓存系统,根据数据热度自动加载热数据,减少跨 AZ 传输,节省专线带宽成本。
QBFS 客户端/服务端:计算引擎通过 QBFS 客户端与 UFS 或 Alluxio 交互,由 QBFS 服务端的挂载配置来动态决定访问具体的存储服务或缓存服务。
热度分析服务:辅助决定哪些表应该加入缓存,充分发挥缓存价值。
缓存探活服务:定期检测 Alluxio 集群的可用性,同步给 QBFS 服务端,帮
助客户端进行异常降级。
缓存管理服务:提供缓存挂载、卸载的接口并保证这个过程中的数据一致性,主动清理过期缓存。
图 4 QBFS-Alluxio 缓存体系架构
Alluxio 集成到 QBFS 涉及到如下组件,使得 Alluxio 可以无缝地与 QBFS 协同工作。
QBFS 客户端:封装Alluxio客户端对上层提供HCFS兼容接口,将命中缓存请求转发到Alluxio。
Alluxio-QBFS 插件:适配支持 Alluxio 以 QBFS 协议访问 UFS, 有两个好处:
图 5 计算引擎透明访问 Alluxio 整体流程
降低跨网络专线的流量,平衡网络带宽和查询性能,比较常见的解决方案是在每个 AZ 可用区内构建本地缓存服务,缓存热数据,从而减少对跨 AZ 数据访问的依赖。
但是在我们的大数据多 AZ 统一调度架构下,如果在每个 AZ 中都独立构建 Alluxio 服务,可能会产生多个 Alluxio 集群间数据不一致的情况。例如,我们的架构中计算任务会根据 YARN 的负载动态调度到不同 AZ 的 Hadoop 集群,同一张表的数据可能写入 Alluxio 集群 1 或 Alluxio 集群 2, 如果没有数据同步机制来维护元数据的一致性,彼此之间写入的数据在对方的 Alluxio 缓存中不可见。但是额外的数据同步机制会增加缓存维护的复杂性。
基于上述理由,我们选择了跨 AZ 的 Alluxio 大集群模式来构建公共缓存层,通过控制写入都走同一个 Alluxio 集群,保证只有一份元数据、从而消除了元数据不一致的可能。我们在 Hadoop 公共服务中搭建了跨 AZ 的 Alluxio 大集群,Worker 分布在多个 AZ 下、Alluxio Worker 与大数据计算节点混合部署,利用计算节点的空闲磁盘资源。
图 8 引入Alluxio后的多AZ统一调度架构
图 9 跨AZ Alluxio 集群下 BlockLocationPolicy 存在的问题
为了解决这一问题,我们扩展了BlockLocationPolicy(Worker 选择策略),实现了 StrictAzPolicy,该策略在读操作时严格考虑客户端所在的 AZ。在新的策略下能够保证数据缓存的地域性,减少跨 AZ 读取带来的延迟和成本。
StrictAzPolicy 策略思路如下:
当客户端所在 AZ 的 Worker 没有缓存数据时,从 UFS 读取数据并缓存在本 AZ 中。我们修改了参数
alluxio.user.ufs.block.read.location.policy.deterministic.hash.shards 的语义,原本该参数用于定义从 UFS 读取数据块时的位置策略,将读取位置分割为多少个副本。现在,我们将其语义调整为在每个可用区 (AZ) 内分割的副本数量,确保每个 AZ 下都缓存至少 1 副本。
客户端总是从所在 AZ 的 Worker 读取缓存,减少跨 AZ 读取带来的延迟和成本。
PageStorage 减少读放大
Alluxio 在 2.8 社区版本及之后引入了更加细粒度的 Page 级缓存存储(最小 1 MB),作为现有 Block 级(默认 64 MB)缓存存储的替代选项。与 Block 级存储相比,Page 级缓存显著减少了 Alluxio 从底层文件系统(UFS)加载数据时的读放大问题。Page 缓存不会增加 Master 的元数据负担,因为 Master 管理的元数据仍然是以 Block 为粒度。Worker 则负责将 Block 切分成 Page 进行存储和管理。
例如,在 Block 存储模式下,每个块的大小为 64 MB,当客户端仅需要读取 Parquet 或 ORC 文件的页脚(通常只有几 KB)时,Alluxio 仍需将整个 64 MB 的块加载到缓存中,这导致了较大的读放大。而采用页面级缓存,由于页面粒度较小,仅需加载一个 1 MB 的页面即可完成相同的读取操作,从而减少了不必要的数据加载,提升了效率和性能。
图 10 PageStorage、BlockStorage 读 UFS 区别
1.TPC-H 读流量测试
我们使用 100 GB 的 TPC-H 数据集测试了不同存储方式下的网络流量情况,TPC-H 的大多数查询涉及大量的表扫描和聚合操作,这使得其工作负载通常以顺序读为主,同时伴有少量的随机读。这种特性更符合离在线计算任务的场景。结果显示 Page 缓存粒度相比 Block 缓存粒度减少了 33% 的读放大。
我们选择使用 1 MB 的 Page 缓存粒度进行存储。
图 11 不同存储粒度在 TPC-H 测试读流量情况
2.Page性能优化
在应用页面级缓存时,我们发现 Alluxio 每次读缓存数据时都会初始化
PagedUfsBlockReader,会进行大量无意义的ByteBuffer.allocateDirect 内存分配(例如,5 MB 页面在一个 SQL 查询中会引发上千次分配)影响性能。为解决这一问题,我们对 PagedUfsBlockReader 进行了优化,只有在真正需要从 UFS 读取数据时才初始化 ByteBuffer。这一优化少了不必要的内存分配,提升了查询性能。详细的优化可以参见 GitHub Pull Request #18390。
以 5 MB Page 为例,TPC-H 测试集在优化前后,Trino 端到端的 P90、P95 及总耗时性能表现对比如下:
图 12 PageStorage 优化前后的 Trino TPCH 性能对比
在具体的应用验证中,例如在爱奇艺某 Hive 查询分析场景中,通过缓存 12 TB 的数据,覆盖了 140 TB 的流量,减少了 90% 的跨网络流量。
03#
Alluxio在爱奇艺数据分析场景的应用
在爱奇艺的近实时分析场景中,我们使用 Trino 和 StarRocks 服务来支持。数据源存储在公共 HDFS 集群中,其承载了大部分离在线计算任务,负载很高、查询的 I/O 波动较大。此外,由于 HDFS 使用的是机械硬盘(HDD),其性能无法满足在线业务的实时需求。为了解决这一问题,我们引入了 Alluxio 来缓存热数据。
图 13 爱奇艺近实时 OLAP 分析服务架构
对于 Trino 和 StarRocks 服务,同一用户的查询会在多个集群中进行负载均衡调度。如果采用 Alluxio 的 Edge 模式(客户端缓存),虽然无需额外的运维成本,但是多集群间缓存无法共享,导致数据冗余,并且无法与 QBFS 集成以复用通用的缓存增强控制。因此,在 OLAP 缓存场景中,我们选择了 Alluxio 的集群模式,通过将 Alluxio Worker 与 Trino Worker 混合部署,充分利用 CPU 和存储资源。
我们为 Trino 移植了软亲和调度功能,以提高缓存本地读的比例。虽然我们期望能显著提升端到端查询性能,但实际结果未如预期。因此,我们对 Alluxio worker 的本地读取和远程读取性能进行了测试。
测试结果显示,读取文件的大小对性能有显著影响。对于 500 MB 的文件,读取性能相比 600 KB 的文件有两倍的提升,尽管两种情况下的读取速度都是毫秒级别,且都非常快。
图 14 LocalRead RemoteRaed在不同文件大小下的读取耗时
在我们的测试环境中,Alluxio worker 并未在硬件资源、服务资源或网络带宽等方面遇到瓶颈。这意味着,在资源未受限的情况下,提高本地读取比例对性能的提升有限。然而,如果在更高负荷或资源受限的情况下,提高本地读取将变得非常必要,以避免潜在的瓶颈并提升整体查询性能。
目前在 OLAP 场景下,我们将线上业务的表主动接入 Alluxio 以获得更快、更稳定的 I/O, 其余表主要是根据热度分析结果,将缓存价值更高的表透明接入 Alluxio。广告数据查询、日志分析、CDN网络监控等多个业务都接入了 Alluxio 缓存,IO密集型的查询性能提速 20 倍,平均性能提升 3 倍。
在广告的流批一体场景中,原来的实时数据通过 Kafka 写到 Kudu,离线数据同步到 Hive,通过 Impala 来统一查询,基于离线覆盖的进度将查询分发到 Kudu 和 Hive。
使用 Iceberg 以后,实时和离线数据都更新 Iceberg,数据写入公共HDFS实现了存储的统一, 任务开发统一为 SQL开发,广告智能出价全链路提速近 5 倍。其中 Trino 查询部分,因为业务数据存储在公共 HDFS 集群,查询 I/O 耗时波动大。在接入 Alluxio 后业务查询 P99 降低 50%。
图 15 广告 Iceberg 流批一体,通过 Alluxio 加速查询
在日志分析场景中,我们将日志从 ES 迁移到 Iceberg 数据湖中,通过 Trino 来查询,大幅节省存储计算成本。但大多数用户都感知到日志查询速度下降(P99 响应时间从 5 秒增加到超过 30 秒)。经过分析 SQL 执行过程,我们发现大部分耗时都在 HDFS 的文件读取上, 因此我们引入 Alluxio 来降低 IO 耗时:
动态缓存管理:根据每张表的访问记录,对访问频繁表的开启缓存,长期未访问的表则关闭缓存。
性能提升:开启 Alluxio 后,SQL 执行时长显著下降,查询稳定性大幅提升,P99 响应时间稳定在 10 秒内。
图 16 引入 Alluxio 前后性能变化
04#
未来计划