首页 时政热点 科技头条 智能AI 安全攻防 数码硬件 开发者生态 归档 标签 关于

快手AB场景提速145倍,从Spark到Apache Doris的加速实践

2026-07-14 1 阅读 约10分钟阅读 SelectDB
分享:
字号:
导读:快手 AB 指标生产场景从 Spark 切换到 Doris 提速 145 倍、资源消耗下降 72%,并刷新了 Doris 单机群最大规模:2000 节点、10 万核。本文介绍快手数据平台团队为什么选择 Apache Doris 构建 AB 实验平台,针对 AB 指标生产场景进行了哪些落地过程中的存储、计算、调度、稳定性等方面优化。快手的干货分享,对需要构建 AB 实验平台的团队有很大的参考价值。 本文整理自快手数据平台部数据引擎技术中心曾斯维在 Apache Doris × 快手北京 Meetup 上的分享。 AB 指标计算链路的性能与成本压力 快手 AB 实验平台是公司级指标计算底座,服务于全公司业务线。任何策略上线前,都需要先在平台中计算实验报告,确认实验结果具备正向收益后,才能继续推进上线。因此,这条链路直接支撑公司级业务决策。 在 Spark 时代,AB 指标计算主要面临两类问题: 计算慢。以核心指标模板为例,单链路计算耗时约 21 分钟。业务同学下午想看的实验结论,往往第二天才能拿到,决策被迫延后。成本高。在 100% 流量推全场景下,计算成本压力已经较大,而实验数量仍在持续增长,计算成本会随规模线性上升。 基于性能和成本考虑,团队将生产链路从 Spark 迁移到 Doris。 目前,快手 Doris AB 集群规模包括 5 个 FE 节点、2000 个 BE 节点、10 万 CU、数百 TB 内存池,并划分为 12 个逻辑计算组。近 14 天审计中,集群承载了数百万个任务,日均任务量达到几十万级,扫描数据量达到 40TB、千亿级行。 在这一规模下,任何低效算子都会被放大,因此必须从底层执行、存储分布、计算算子和调度治理等多个层面系统优化。 为什么 Doris 比 Spark 更适合该场景? Doris 相比 Spark 的优势,主要来自三方面执行模型差异。 第一,Pipeline 执行引擎减少线程等待。 Spark 在两个 Stage 之间进行 Shuffle 时,数据通常需要落盘,下游必须等待上游全部完成后才能继续执行,线程存在阻塞。Doris 采用 Pipeline 模型,在等待数据时可以让出线程去执行其他任务,减少 CPU 空转。 第二,向量化执行提升批处理效率。 Spark SQL 内部使用行式 Internal Row,而 Doris 是全链路列式 Block,每 4096 行作为一批进行处理。列式布局更适合 CPU SIMD 批量处理,函数调用也可以从每行一次摊薄到每批一次。 第三,C++ 运行时降低 JVM 相关开销。 Spark 运行在 JVM 上,存在 GC Stop-The-World 和 JIT 开销。Doris 使用 C++ 编译后的机器码执行,没有 JVM GC,启动后即可进入较高执行效率。 不过,这些只是通用能力。对于快手 AB 指标计算而言,更大的收益来自业务计算模型与 Doris 执行机制之间的匹配。正因为 AB 指标计算模板长期稳定,团队才有机会围绕执行链路进行更深入的专项优化,最终达到 145x 性能提升。 关键优化:从减少数据流动到压缩计算热路径 完成引擎迁移后,团队没有只停留在替换执行引擎层面,而是进一步围绕 AB 指标计算模板的特点,对数据分布、执行计划、计算热点、资源调度和元数据治理进行了针对性的系统优化。最终,多项优化共同作用,释放了 Doris 在该场景下的性能潜力。 AB 指标计算链路的结构相对稳定,输入通常固定为两类数据:一类是累计分流表,用于记录用户命中了哪个实验、哪个分组;另一类是指标宽表,用于记录用户行为指标。输出也相对固定,通常按照实验、分组、桶等维度进行聚合,结果集一般只有百行到万行。 完整计算过程主要包括四步: 1)扫描累计分流表,按日期和实验名过滤命中信息; 2)扫描指标宽表,按 UID 进行预聚合,并处理指标口径; 3)两张表按 UID Join,并做桶粒度聚合; 4)将桶粒度结果上卷,写入实验结果表。 其中,第二步和第三步是优化主战场,因为这里同时涉及 Join、分组聚合、网络传输和 CPU 计算开销。 图片这个场景最关键的特点是:Join Key 长期固定为 UID,聚合维度也相对稳定。换句话说,这不是一个随机查询模式高度复杂的 OLAP 场景,而是一个计算模板长期稳定、执行路径高度一致的生产计算场景,因此具备持续专项优化的条件。 3.1 存储优化:用 Colocate Join 消除跨节点 Shuffle 第一项核心优化是 Colocate Join。它的基本思路是:在数据写入阶段,就按照 UID 对数据进行哈希分桶,使相同 UID 的数据始终落到同一台机器、同一个分桶内。 这样做的好处是,查询时分流表和行为表可以直接在本地完成 Join,不需要跨节点 Shuffle 或搬运大批量数据。每个节点只需处理本地分桶内的数据,并完成本地聚合,最后再汇总少量桶级结果。 从生产数据看,单个 Bucket 扫描千万级数据后,本地 Join 可以先过滤掉约 95% 的无效数据,只保留几十万行匹配结果,最终输出几千行。也就是说,大部分数据在本地节点内就被消化掉了,网络中只传输 Join 和聚合后的少量结果集。 落地 Colocate Join 时,需要重点保证两点: 建表时必须按照 Join Key,也就是 UID,进行哈希分桶;参与 Join 的两张表,分桶数必须严格一致,不能有任何偏差。 表结构调整完成后,还需要通过 EXPLAIN 检查执行计划。只有执行计划中出现 Colocated,才说明该优化真正生效。如果计划中仍然存在 Shuffle,通常意味着两张表的分桶数、分布键或 Colocate 配置没有对齐,需要继续排查。 3.2 计算优化:降低去重聚合与 UDF 热路径开销 3.2.1 Local Distinct Grouping Sets:减少全局 Shuffle Colocate 优化已经消除了 Join 带来的跨节点 Shuffle,但在计算层中,去重算子仍然可能引入新的全局 Shuffle 开销。 在 AB 指标计算中,SQL 广泛使用 Grouping Sets,因为单条查询需要同时输出多个维度组合的聚合结果。在 Doris 的原生执行框架中,普通 Distinct 的两阶段优化无法覆盖 Grouping Sets + Distinct 的组合场景,因此仍然会触发全局 Shuffle,从而带来较高的网络开销和内存峰值压力。 针对这一问题,团队设计并实现了 Local Distinct Grouping Sets 改写机制。其核心思想是:在 Colocate 分桶已保证同一 UID 数据局部聚集的前提下,将去重计算前移至各计算节点本地执行,先完成局部 Distinct,再对局部结果进行全局聚合汇总,从而在语义等价的前提下降低 Shuffle 成本。 该优化提供两种使用方式: 透明改写模式:优化器自动识别 Grouping Sets + Distinct 模式,并重写为本地计算路径,业务 SQL 无需改动; 显式调用模式:业务侧可通过 Distinct Local 语法显式指定本地去重,用于优化器
这篇文章对您有帮助吗?

订阅66必读

每日精选科技资讯,直达你的邮箱