快手 AB 指标生产为什么能从 Spark 迁到 Doris 并提速 145 倍

2026-07-08 25 预计阅读时间: 1 分钟
来源: my.oschina.net AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

快手 AB 实验平台的指标生产链路,从 Spark 切换到 Apache Doris 后,性能提升 145 倍,资源消耗下降 72%。更值得注意的是,这不是一个小规模验证:相关实践刷新了 Doris 单集群规模,达到 2000 节点、10 万核。对做实验平台、指标平台、用户行为分析的团队来说,这个案例的价值不只在“快”,而在于它说明了什么时候该把离线批处理思路,换成面向分析服务的计算与存储模型。

AB 指标生产的瓶颈不只是计算慢

AB 实验指标生产通常有几个特点:

  • 数据量大:曝光、点击、播放、消费、留存等行为日志会持续增长。
  • 维度多:实验、分组、版本、地域、设备、用户分层、内容类型都可能成为聚合维度。
  • 指标口径复杂:均值、转化率、分位数、留存、去重用户数、分桶统计会混在一起。
  • 产出频繁:实验平台需要不断刷新指标,支持看板、告警、实验结论分析。

Spark 很适合大规模批处理,但在 AB 指标生产里,链路经常会变成“反复扫描、反复 shuffle、反复写中间结果”。当指标口径和维度组合膨胀后,调度开销、资源排队、数据重算和小文件问题都会把整体时效拉长。

Doris 的优势在于它把列式存储、MPP 执行、向量化计算和高并发查询放在一个分析型数据库里。对 AB 指标这种“按实验维度持续聚合、反复查询”的场景,很多计算可以被下推到存储和执行引擎中完成,减少外部批处理链路里的数据搬运。

从 Spark 到 Doris,变化发生在链路形态上

这类迁移不是把一段 Spark SQL 原样塞进 Doris 就结束。真正的变化通常在四个层面。

存储层需要围绕查询模式建表。AB 指标表往往适合按日期、实验 ID 等字段分区或分桶,并根据常用过滤条件设计排序键。这样查询单个实验、某天增量或某批实验时,可以少扫很多数据。

计算层从“大任务批量扫全量”变成“明细写入、预聚合、按需查询”。如果指标口径稳定,可以把部分聚合结果沉淀成宽表或聚合表;如果口径变化频繁,则保留更细粒度的明细层,再用 Doris 查询能力承接交互式分析。

调度层要从 Spark 作业依赖,转向数据导入、物化结果刷新和查询服务之间的协调。比如小时级导入行为日志,分钟级刷新核心实验指标,低频补算历史数据。

稳定性层变得更重要。快手案例中提到的 2000 节点、10 万核规模,意味着问题已经不只是 SQL 优化,而是集群治理:导入压力、Compaction、查询并发、资源隔离、失败重试都要被系统化处理。

可以这样实践:用 Doris 建一个 AB 指标雏形

下面示例不是快手内部实现,而是一个可改造的最小实践:用 Doris 存放 AB 事件明细,并查询实验组核心指标。你需要把 fe_host、端口、用户名、密码替换成自己的 Doris 环境。

创建库表:

CREATE DATABASE IF NOT EXISTS ab_platform;
USE ab_platform;

CREATE TABLE IF NOT EXISTS ab_event_detail (
    event_date DATE NOT NULL,
    experiment_id VARCHAR(64) NOT NULL,
    variant_id VARCHAR(32) NOT NULL,
    user_id BIGINT NOT NULL,
    event_name VARCHAR(64) NOT NULL,
    value DOUBLE DEFAULT 0,
    event_time DATETIME NOT NULL
)
DUPLICATE KEY(event_date, experiment_id, variant_id, user_id)
PARTITION BY RANGE(event_date) ()
DISTRIBUTED BY HASH(experiment_id, user_id) BUCKETS 16
PROPERTIES (
    "replication_num" = "3",
    "dynamic_partition.enable" = "true",
    "dynamic_partition.time_unit" = "DAY",
    "dynamic_partition.start" = "-30",
    "dynamic_partition.end" = "3",
    "dynamic_partition.prefix" = "p",
    "dynamic_partition.buckets" = "16"
);

写入几条测试数据:

INSERT INTO ab_event_detail VALUES
('2025-01-01', 'exp_checkout_button', 'A', 1001, 'exposure', 1, '2025-01-01 10:00:00'),
('2025-01-01', 'exp_checkout_button', 'A', 1001, 'click', 1, '2025-01-01 10:00:02'),
('2025-01-01', 'exp_checkout_button', 'B', 1002, 'exposure', 1, '2025-01-01 10:01:00'),
('2025-01-01', 'exp_checkout_button', 'B', 1003, 'exposure', 1, '2025-01-01 10:02:00'),
('2025-01-01', 'exp_checkout_button', 'B', 1003, 'click', 1, '2025-01-01 10:02:05');

查询每个实验组的曝光用户、点击用户和点击率:

SELECT
    experiment_id,
    variant_id,
    COUNT(DISTINCT IF(event_name = 'exposure', user_id, NULL)) AS exposure_users,
    COUNT(DISTINCT IF(event_name = 'click', user_id, NULL)) AS click_users,
    ROUND(
        COUNT(DISTINCT IF(event_name = 'click', user_id, NULL)) /
        NULLIF(COUNT(DISTINCT IF(event_name = 'exposure', user_id, NULL)), 0),
        4
    ) AS ctr
FROM ab_event_detail
WHERE event_date = '2025-01-01'
  AND experiment_id = 'exp_checkout_button'
GROUP BY experiment_id, variant_id
ORDER BY variant_id;

如果你希望通过命令行跑通,可以这样执行:

mysql -h fe_host -P 9030 -u root -p < ab_metric_demo.sql

在生产里,这个模型还需要继续补强:明细表按事件日期分区,按实验和用户分桶;高频指标可以落到汇总表;历史补算和实时导入要分开资源组;核心看板查询要做超时、限流和缓存。

迁移时最容易低估的四个问题

指标口径一致性是第一道门槛。Spark 迁 Doris 后,去重、空值、时间窗口、延迟数据处理方式都要对齐。不要只比较总行数,要抽样比较实验、分组、日期、核心指标的差异。

导入吞吐和查询并发会互相影响。AB 平台通常一边写入行为数据,一边服务看板查询。大规模导入会触发后台整理,查询高峰又会抢 CPU 和 IO。需要用资源隔离、导入批大小、分区策略一起治理。

表设计决定上限。Doris 能跑大规模集群,但错误的分区和分桶会让查询扫过多 tablet,或者让数据倾斜集中到少数节点。AB 场景里,实验 ID、日期、用户 ID 往往是关键设计字段。

调度不能只追求更快。Spark 作业失败通常有清晰的批任务边界;迁到 Doris 后,失败可能出现在导入、Compaction、物化视图刷新、查询超时等多个环节。调度系统要能重试、补偿、追踪每个指标版本。

落地建议:先迁最稳定、最高频的指标

这类迁移适合从高频、口径稳定、查询压力大的 AB 指标开始,而不是一口气替换所有 Spark 作业。可以按这个清单推进:

  • 选 3 到 5 个核心实验指标,建立 Spark 与 Doris 双跑对账。
  • 用真实查询日志设计分区、分桶、排序键,而不是只看数据写入方式。
  • 把导入、补算、看板查询放进不同资源池,避免互相拖垮。
  • 为每个指标保留口径版本,迁移期间允许按版本回溯。
  • 先验证 P95/P99 查询耗时、资源消耗和失败率,再扩大实验范围。

快手这个案例说明,AB 指标生产不一定要长期绑在通用批处理引擎上。当场景从“离线算一次”变成“持续生产、频繁查询、多人同时看结果”,Apache Doris 这类实时分析数据库会更贴近问题本身。不过,145 倍提速来自场景匹配、链路重构和大规模工程优化的叠加,不是简单替换 SQL 引擎就能复现。真正值得学习的是:围绕指标生产的访问模式重新设计系统,而不是只给旧链路换一个执行器。


相关推荐