下载研究报告
CHAPTER 07 · TECHNICAL / PLATFORM

核心技术模块(下):指标、架构与组织

平台架构的难点不在报表,而在控制面、数据面、分析面的失败模式不同;不能用同一个可用性目标约束三个平面。

12,095 字 24 分钟 95 条引用

7.1 指标体系与 OEC#

统计引擎决定「算得准不准」,指标体系决定「算的是什么」。OEC(Overall Evaluation Criterion)常被误解为「一个总分」,实际是一套决策结构:实验前约定用什么指标判断成败,指标冲突时谁优先。Kohavi 等在《Trustworthy Online Controlled Experiments》第 7 章要求 OEC 实验前确定、对改动敏感、平衡短期与长期,并能解释「为什么这个改动值得上线」来源。

OEC 有三种典型误用:把北极星指标直接当 OEC(太滞后、太宏观,单个实验两周内推不动)、把 OEC 做成加权评分卡(权重拍脑袋,权重一改历史实验就不可比)、用 OEC 替代 guardrail(OEC 变好不代表可以上线,短期收入可能是在透支用户信任)。可操作的结构是:1 个主指标(决定 ship/no-ship)+ 2–4 个 driver(解释主指标)+ 一组 guardrail(一票否决)+ diagnostic/counter(排障与对账)。主指标用比率或人均值,不用总量,总量会被流量波动污染。

类型 回答的问题 决策权 典型例子 常见失败模式
Success / OEC 这个改动整体是否更好? 有,最终裁决 人均订单额、7 日留存 太滞后、被 novelty 污染
Guardrail 有没有把什么弄坏? 有,一票否决 崩溃率、P99 延迟、退款率 功效不足、方向搞反
Diagnostic 为什么好、为什么坏? 无,只解释 漏斗各步转化、分渠道 CTR 被当成决策指标、只挑显著切片
Counter 数量对不对、链路通不通? 无,只对账 曝光 UV、进组 UV、订单事件数 与 success 混用、重复计数

指标不是越多越好。看 20 个指标、每个用 5% 显著性水平,至少一个假阳性的概率是 1-0.95^20 ≈ 64%。成熟平台不是限制数量,而是分层写进流程:OEC 和 guardrail 实验前注册,diagnostic 事后加但标注「探索性」,counter 只进数据质量面板。OEC 的真正功能不是打分,而是把「什么结果对应什么动作」提前写死。

「口径不一致」大多不是 SQL 写错,而是定义缺字段。工业级指标契约至少写清六组:标识与版本(metric_key、语义化版本、认证级别)、业务含义(一句话定义 + 反例)、分析实体(user/session/device/account)、聚合与分母(ratio 必须显式写分母)、时间窗口(归因窗口、聚合粒度、时区)、治理信息(owner、分层、血缘、行数/空值/分布断言)。缺一组,就会出现同名不同义、人均值和会话均值混用、转化率分母差十倍、历史报告悄悄漂移。

行业已经收敛出「事实表 + 指标定义」这个抽象。Eppo 把数据模型拆成 facts、assignments、entry points、entity properties;事实表只需「时间戳 + 实体标识」,指标在其上叠加聚合方式,比率、漏斗、分位数是独立类型来源来源。GrowthBook 的 Fact Table 同样要求 timestamp 和至少一个标识列,并明确推荐新指标都用 Fact Table,而不是旧的「每个指标一段 SQL」模式来源。治理路线有三条:Airbnb Minerva 的集中式指标商店(第三方综述称 12,000+ 指标、4,000+ 维度、200+ 数据生产者)来源、Netflix/Eppo 的指标即代码、LinkedIn UMP/Uber uMetric 的指标 API。共同点是:指标是有 owner、有版本、有测试、有 SLA 的软件资产;区别只在集中程度。Eppo 把「信任」产品化成 Certified 徽章:simple、ratio、percentile 可认证,funnel 不可来源。

版本化要做四件事:不可变、钉住(实验创建时记录指标版本)、可回填、可对比。Eppo 的增量任务默认回看最近 48 小时,先删除最近两天再重插,以吸收迟到数据;新指标或定义更新会自动从实验开始回填来源。这里要分清:48 小时是回看窗口,不是实验窗口。所有权要分业务 owner、数据 owner(pipeline、freshness、断言、on-call)、平台 owner(目录、权限、版本、回填工具)。多数公司处在 L1–L2:有指标字典,但定义与计算分离;直接从 L2 跳到 L4 往往失败,因为组织还没有「指标 owner」这个角色。

指标不是报表里的一个字段,而是有 owner、有版本、有分母、有排除规则、有质量分的软件资产;OEC 不是加权公式,而是「主指标 + 护栏 + 预注册决策」的组织契约。

7.2 曝光、归因与数据链路#

分流 bug 通常当天能被发现,曝光 bug 可能潜伏半年,最后以一个「看起来显著、上线后消失」的结论收场。实验平台最脆弱的一厘米,就在 assignment(分配)与 exposure(曝光)之间。

三个概念必须分开:Assignment 是分流服务按 hash 把实体分到某个 variant,回答「如果这个用户来了,他应该看到什么」;Exposure 是用户实际看到了实验版本,回答「这个用户真的被处理了吗」;Trigger / Entry Point 是用户满足「可能被影响」的条件(进入搜索页、加购、打开弹窗),回答「从哪一刻开始算实验的影响」。三者的时间顺序通常是 assignment → exposure/trigger → outcome,但工程上经常错位:首页就分配了所有实验,用户可能永远不进商详页;服务端在请求入口求值 flag,页面却白屏;客户端缓存旧配置,曝光延迟几分钟才上报。Eppo 的官方表述很直接:有些实验在首页完成分配,但用户只有进入特定页面才真正暴露;可以用 Entry Point(又称 qualifying event)过滤分配源,是否纳入实验、指标计时起点,都以 entry point 的时间戳为准来源。分配表负责「谁有资格」,入场点负责「从何时开始算」。

统计上更稳妥的默认是 ITT(intent-to-treat):按分配时的 variant 分析所有人,不管有没有真的曝光。ITT 保守(把没曝光的人算进分母,效果被稀释),但无偏,因为分配是随机的,曝光不是。按曝光分析(per-protocol)看起来更「精准」,但曝光是治疗后变量:用户是否看到新版本,可能受新版本速度、崩溃、引导流程影响;按曝光过滤会把「因为新版本太慢而离开的人」排除掉,反而制造乐观偏差。实践建议:主分析用 ITT;同时报一个「曝光人群」的敏感性分析,明确标注它不是因果主结果;如果 assignment 与 exposure 差异超过 20%,先修分流/曝光链路,而不是靠统计方法补救。当差异超过 20%,它已经是工程问题,不是统计问题。

曝光重复上报是常态:页面刷新、SDK 重试、离线包补发、多个埋点同时触发。去重规则必须在平台层固定。Eppo 的分配表去重是标准三件套:对同一 subject–experiment 对只取实验日期范围内的第一条记录;如果一个 subject 出现多个 variant,把这些 subject 从结果中剔除,并在过滤比例超过阈值时告警;分配属性取「首次分配时」的值,避免用户旅行/换城市导致属性漂移,也避免 CUPED 用到未来信息来源。这三点分别对应重复、冲突、时点泄漏。

字节 DataTester 的公开实践更直接:SDK 调用分流方法时上报一条「实验曝光事件」,记录进组信息;后续指标计算认为「发生在进组之后的事件」受实验版本影响;曝光可能反复上报,只有第一条曝光是有效事件。他们后来把曝光从事件表里拿出来,转成用户表上的属性,原因是:曝光事件和普通事件混在一张表里量级太大,找第一条记录会随实验时长扫描越来越多分区,重复曝光还要额外去重;改成用户属性后,用户表规模只随新增用户增长,还少了一次 join,14 天以上实验的多天累计报告查询时长下降 50% 以上来源。另一篇优化文章提到,从实时流过滤曝光事件构建 ab_log、再与离线天粒度进组表按「最早进组时间」取 min,在部分客户环境减少 30% 以上的曝光事件量来源。

这个优化值得展开:曝光不是「又一条事件」,而是「用户的一个状态」。实验周期越长、事件量越大,把首次曝光物化成用户级宽表越划算;代价是丢掉逐次曝光的 surface/request_id 上下文,排障能力下降。成熟做法是事件表保留 7–30 天做诊断,用户状态表做累计分析。

身份是第二个深水区。真实关系是多对多:一个 anonymous_id 可能对应多个 user_id(共用设备),一个 user_id 也可能对应多个 anonymous_id(多设备、清 cookie);用户可能在实验中途登录,曝光在匿名态、转化在登录态。Eppo 的做法是:按 anonymous_id 取实验期内第一条记录,多 variant 则剔除;建立 anonymous_id→user_id 映射,每个 user_id 只关联实验期内第一个 anonymous_id;最后同时 join 匿名态和登录态指标来源。代价是可能轻微违反 i.i.d.,但能避免「剔除多 ID 用户」的选择偏差——多 ID 用户往往更活跃,剔除会系统性低估效果。这是偏差换方差,没有免费解。

归因窗口有起点和长度两个维度。起点用 assignment_ts 还是 exposure_ts/entry point,决定「早曝光晚转化」算不算;Eppo 以 entry point 为准。长度可以是 7/14/30 天、生命周期或「实验结束后 N 天」,Kameleoon 把 attribution window 做成独立配置项来源。短窗口低估延迟转化(B2B、保险、大家电),长窗口更接近 LTV 但实验更久、后期污染更多。右删失最隐蔽:实验结束前最后几天曝光的用户没有完整 30 天窗口,直接算会低估;应固定「曝光后第 N 天」cohort 或等窗口跑满。建议默认 14 天(电商/内容)或 30 天(订阅/金融),实验前注册,同时报 7/14/30 天敏感性。

曝光日志最小 schema:exposure_id(幂等键,hash(subject_id, experiment_key, variant, exposure_ts))、subject_id + id_type、experiment_key + variant(不要只存 flag name,flag 会复用)、assignment_ts、exposure_ts、surface/page/module、session_id、model/config_version、request_id、consent/bot_flag。尤其是 assignment_ts 与 exposure_ts 必须分开:只有曝光时间,无法区分「分配了没曝光」和「曝光了但没分配」;只有分配时间,无法判断曝光延迟。

SRM 是实验的体温计,不是病:观测分组比例与设计比例显著不符,通常不是随机波动,而是管道 bug。开源 SRM Checker 用 p<0.01来源;LinkedIn 约 10% 的 triggered experiments 曾出现 SRM,Microsoft 约 6%,一年一万个实验的产品平均每天至少一次来源。没有 SRM 告警,往往不是没问题,而是没检测。KDD 2019 把成因分五类:实验分配、执行、日志处理、分析、干扰来源。最易忽略的:HLL++ 近似 UV 会频繁触发 SRM;treatment 新增埋点让「至少有一条事件」的用户更容易被记录,是可见性变高不是用户变多;treatment 更慢/更易崩,用户会在日志生成前离开,SRM 与指标下降同链。原则:SRM 未解释前不读效果指标,不用重新加权掩盖,只有缺失可证明随机才考虑回填或极端值边界。

数据链路的三条契约——分配、曝光、指标——比任何单一引擎都重要。曝光应按「事实表」设计:幂等、可对账、带版本,而不是「顺手打一条日志」。

7.3 实时、批处理与 warehouse-native#

团队说「我们要实时」,通常混了三件事:摄入实时(事件几秒内进 Kafka/OLAP)、指标实时(转化率、人均 GMV 分钟级更新)、决策实时(统计检验、guardrail、SRM 分钟级刷新,能据此自动停实验)。第三层最难也最贵。绝大多数实验不需要秒级决策;需要秒级的是护栏与健康度(崩溃、延迟、支付失败)以及 SRM 早期预警。把「决策实时」当默认需求,会直接把架构推向全链路流式,代价是回填、维表 join、口径一致性都变难。

架构 做法 延迟 优点 代价 典型
Lambda 实时 OLAP + 离线数仓双链路 秒–分钟 + 天 实时看板快,离线口径全 两套 SQL/两套口径,容易对不上 DataTester SaaS
Kappa 全流式,Kafka/Flink 计算,失败重放 秒–分钟 一套逻辑,延迟低 维表 join、回填、历史重算困难 实时风控/推荐
仓库原生 定义在数仓,SQL 编译下推,只出聚合 分钟–小时 口径统一、可审计、易回填 依赖数仓并发/成本,交互延迟较高 Eppo、Statsig WHN、GrowthBook

字节 DataTester 的私有化架构是一个很好的 Lambda 案例:SaaS 采用「实时 + 离线」的 Lambda 架构;私有化为了减少资源开销、适应中小集群,只保留实时部分,由计算引擎对上层屏蔽差异;报告基于 ClickHouse 实时分析;分流服务用内存、Redis、DB 三级缓存,消息队列保证多节点一致,另有定期全量刷新兜底来源。这说明「实时」不是免费的:私有化客户用「放弃离线链路」换资源,代价是历史重算和复杂分析的灵活性。

ClickHouse 擅长单表查询和多维聚合,join 是弱项;要么把 join 提前做成宽表,要么把右表做小,避免大表拉本地建 hash来源。DataTester 的做法:预聚合人粒度 user_agg(100–500 事件/人 → 1/100–1/500;4 亿事件、100 万用户查询提升 4 倍+);ab_log 把「用户+进组时间」写实时表,再与离线天表按最早进组时间取 min,部分客户曝光事件量降 30%+;不同 variant 合并 GroupBy;AU 缓存/异步/Redis 把刷新压到 100ms。前提是指标、维度、版本可枚举且以天级累计为主;任意维度/时间窗探索会让预聚合失效。代价是更新删除不友好、join 弱、并发争抢、分区排序键复杂;适合实验报告,不适合唯一数仓。

Druid 的定位更窄:面向实时摄取、快速查询、高可用 OLAP;典型查询延迟从亚秒到几秒;适合事件型数据、高基数、时间序列、聚合报表;不支持按主键低延迟更新已有记录;不适合大表 join 大表;支持近似 count-distinct、近似分位数来源。这几乎是「实验实时看板」的完美候选,但不适合「归因窗口 + 多维 join + 精确财务对账」的最终口径。合理分工是:Druid 做实时健康度/趋势,数仓做最终决策口径。

BigQuery 的 Storage Write API、Snowflake 的 Snowpipe Streaming 正在把流式写入做进数仓。Snowflake 在 2025-09-23 宣布 Snowpipe Streaming 的高性能架构 GA来源;BigQuery 也提供 Storage Write API 的流式写入路径来源。但进仓快 ≠ 查询快:高并发、复杂 join、窗口函数仍然要扫大量数据,成本随扫描量上升。仓库原生的真正优势不是延迟,而是口径唯一、可审计、可回填、复用数仓行/列级权限且 PII 不出仓;代价是交互体验,分析师从 ClickHouse 的秒级拖拽回到 BigQuery/Snowflake,可能要等几十秒到几分钟。

Eppo 是典型的仓库原生:整个 pipeline 是跑在客户数仓里的 DAG,只有最终阶段的匿名聚合结果出仓;分配汇总、核心指标、CUPED、维度、漏斗五条分支;增量刷新默认回看 48 小时,定义变更自动从实验开始回填;CUPED 用实验前 30 天数据做回归;高基数维度截断到 50 个值来源。另有文档显示其默认刷新节奏是 24 小时来源。这里要分清:48 小时是增量回看窗口,24 小时是报表刷新节奏,两者不是同一件事;把它们混为一谈,会得出「Eppo 只能看两天」的错误结论。

仓库原生的成本是换了计费口。Statsig 给过一组透明基准:仓库成本通常占平台合同 5%–20%;小实验(≤5 万单元)单次加载 <$0.05、4 周生命周期 $0.25–$2;大实验单次 $0.25–$10、生命周期 $5–$280;并称其 WHN 成本是其他平台的 50%–66%(厂商自述,需第三方验证)来源。反例:客户 PoC 用未分区表,成本是竞品 120%;分区后 <60%。成本高度依赖数据建模;实验数、指标数、维度基数、刷新频率是四个杠杆,选型要按自己的仓做 PoC。

WHN 不划算的场景:需要分钟级/秒级决策或自动止损;实验密度极高、每个都跑全量历史,查询费超过订阅费;数仓很乱(无分区、无 dbt、口径不一),WHN 会放大问题;没有数据工程维护 SQL/权限/成本/回填;多仓/多云,一套 WHN 通常绑一个主仓。治理上,WHN 把数据访问权交给平台服务账号,必须做列级权限、PII 脱敏、查询审计、行级过滤,否则实验平台会成为绕过数仓权限的后门。

一句话选型:日级决策、指标多、要回填,选仓库原生;实时护栏、SRM 预警,选 ClickHouse/Druid;私有化资源有限,选 ClickHouse 单链路 + 预聚合;复杂归因、跨表 join,回数仓批处理。不要试图用一套引擎同时满足「亚秒交互」和「可审计回填」。实时/批处理的真正分野不是延迟,而是「指标定义在哪里、能否回填、谁能审计」;正在收敛的形态是「仓库原生做决策口径 + 轻量 OLAP 做实时护栏」的混合架构。

7.4 参考架构:控制面、数据面、分析面#

实验平台表面上都在做「分流量、看结果」,内部却运行着三种完全不同的负载。控制面写少读多、强一致、低 QPS,坏了只是「不能改」,不能让正在运行的实验停摆;数据面读多写少、极高 QPS、最终一致,坏了必须继续服务,最多返回旧配置或默认值;分析面吞吐大、延迟容忍度高、可重跑,坏了不影响线上,但会让决策停摆。三者不能用同一套数据库、同一套 SLO、同一套发布节奏。

平面 核心职责 一致性 典型组件 故障影响 扩缩单位
控制面 实验/Flag 元数据、审批、RBAC、审计、版本、调度 强一致 元数据 DB、工作流、版本库、管理 API 不能新建/修改,已运行实验继续 租户/项目,垂直扩
数据面 配置分发、边缘缓存、SDK 求值、曝光采集 最终一致/可用优先 分发服务、Relay/Edge、SDK、消息队列 返回旧配置或默认值,可能丢曝光 请求量/连接数,水平扩
分析面 事件入湖、指标、统计、报表、护栏 最终一致/可重跑 Kafka、湖仓、Spark/Presto/ClickHouse 结果延迟,不影响线上 数据量/实验数,批处理扩

Microsoft ExP 的公开架构被总结为实验门户、执行服务、日志处理、分析服务四部分来源,是三平面的雏形;Netflix XP 把分析面拆成 Metrics Repo、Causal Models、XP Viz 和 Platform API,并让分析代码可在本地 Jupyter 重跑,以保证可复现来源;美团 2024 年把实验设计/配置/流量/数据处理与分析方法库解耦来源。

三条硬边界必须写进架构决策:热路径不查库、不调分析服务,控制面把配置编译成不可变快照,数据面只读快照,分析面只消费异步事件;分析面不直接读在线控制库,所有结论必须基于「实验版本 + 配置版本 + 曝光/指标数据」的不可变快照,否则今天跑出的结论明天无法复现;配置版本与实验版本关联但不等同,配置版本回答「用户当时看到什么」,实验版本回答「统计上比较的是什么」,扩流、改规则、换变体都应产生新的实验版本,分析时按版本和时间窗切分。否则 10% 流量和 50% 流量的样本混在一起算 SRM,一个百分比就把偏差洗白了。

对象模型至少包含 Tenant/Project/Environment、Flag、Experiment、Layer/Domain、Metric/Guardrail、Approval/AuditEvent。关键是:Experiment 不是 Flag 的同义词,而是「Flag 版本 + 统计计划」的组合对象。Flag 可以独立存在、独立回滚;Experiment 必须有开始/结束和决策,否则就是一笔糊涂账。生命周期建议 draft → in_review → approved → scheduled → running → paused → concluded → archived,旁路 rejected/aborted/rolled_back;没有假设、指标、护栏、MDE、负责人不许提交评审,没有双人审批不许进生产,没有 SRM/护栏检查不许自动结论,没有 Flag 清理计划不许归档。

图 2 · 工业级实验平台参考架构

热路径应是:请求 → SDK 读取原子快照 → 取 unit_id → 按 layer/salt 哈希 → 查区间 → 得到 variant → 曝光写有界队列(不阻塞)→ 返回;后台批量压缩上报 → 消息队列 → 湖仓。整条路径没有数据库、没有 RPC、没有 JSON 解析;最坏情况是队列满,按采样/丢弃处理并打点 exposure_dropped。「无状态」的真正含义是:正确性来自快照版本,而不是某台机器的内存状态。 三个平面的失败模式不同,所以不能用同一个可用性目标;「控制面正确、数据面可用、分析面可复现」是架构宪法,而配置、实验、指标三个版本号是结论可复现的审计线索。

7.5 分流服务与配置下发#

10 万 QPS 的最优解是「没有中心分流服务」:规则下发到 SDK/边缘,本地求值;中心只承担配置分发和治理。可信分流要满足确定性、均匀性、稳定性、正交性、可追溯。Google 的重叠实验设施把参数划分到多个层,层内互斥,层间用 mod = f(cookie, layer) % 1000 保证正交来源。工程上可简化为 bucket = murmur3(salt + ":" + unit_id) % 10000;salt 至少包含 experiment_id + version,同层共享互斥区间,不同层用不同 salt。不要用 hash(unit) % 100 分所有实验,否则实验之间天然相关。

三级缓存:L1 在 SDK 进程内放编译规则快照(>99% 求值,原子指针)、L2 在边缘/Relay/Redis 放环境快照(流式 + TTL)、L3 在对象存储/DB 放不可变版本(冷启动/灾备,版本寻址)。LaunchDarkly Relay 是客户机房里的 Go 应用,daemon 模式要求 SDK 与 Relay 共享持久化存储来源;Unleash Edge 是内存缓存 + 流式/轮询,官方称单实例可处理「数万到数十万 RPS」来源;GrowthBook Proxy 用内存 + Redis/MongoDB,靠 Webhook 失效来源。共同点:读离应用足够近,写留在中心。缓存要配 singleflight、stale-while-revalidate、磁盘快照;边缘重启没有本地快照,十万 SDK 同时回源会把中心打挂。

多区域是「读本地、写归属、版本单调」:每区完整只读副本,SDK 连最近区域,故障切邻区;控制面写租户 home region,跨区异步复制;冲突以 version 大者为准,同版本按 region priority,不依赖时钟;断连时用本地快照,恢复后从 last_known_version 增量追平,缺口太大全量拉取。数据驻留要求 EU/中国租户数据留在本区域,全局控制面只做路由。p99 预算:SDK 本地 <1ms、sidecar <3ms、边缘 <10ms、控制 API <300ms。尾延迟的隐藏杀手是可观测性本身:求值路径只做计数器和直方图,曝光走异步队列,高基数维度放日志不放指标标签。

fail-open 是每个 Flag 的产品决策:实验回退 control 并标记 assignment_source=default;发布开关回退 last-known-good;Kill Switch 回退 off;支付/风控 fail-closed;推荐回退热门。关键:实验回退 control 会损失功效,若故障与区域/设备相关,缺失不是随机的,会产生偏差;要记录 fallback_reason、config_version、region,分析时标为「降级期」。LaunchDarkly 用 SDK 缓存、CDN 缓存、代码默认值三层兜底来源;OpenFeature 要求求值不抛异常,异常返回默认值来源。

配置下发是「流式为主 + 轮询兜底 + 周期全量对账」。LaunchDarkly Go SDK 有 Default/Streaming/Polling/PersistentStore/Daemon 五种模式,轮询默认/最小 30 秒来源;Splitd 默认 streaming,轮询 Flag 30 秒、Segment 60 秒来源。下发单位是不可变快照:manifest 带 schema_version、config_version、flags[version, checksum, blob_ref]、signature;增量带 base_version/new_version,不匹配就全量;payload 签名,验签失败拒绝更新。版本按对象单调递增,全局版本只用于对账,不让时间戳充当版本。传播 SLO 独立定义:commit → 99% 边缘可见 p99 <5 秒,→ 99.9% SDK 可见 p99 <30 秒;「配置很旧但服务正常」会让用户分到旧版、曝光记新版,分析直接错位。配置分发的正确性单位是不可变快照,不是消息;一致性判据是版本号,不是时钟。

7.6 SDK 与 OpenFeature#

SDK 是平台唯一进入业务进程的代码,同时是性能组件、数据采集器和一致性边界。第一原则是求值不走网络:启动拉全量快照,之后进程内哈希/规则匹配。Unleash 官方称后端 SDK 求值是「纳秒级」来源;LaunchDarkly 也本地缓存。四个工程问题:规则编译(JSON → AST/字节码)、原子切换(新快照构建后替换指针)、内存上限(按 project/flag set 懒加载)、多语言一致(哈希/比较/类型转换一致,用 conformance 用例)。缓存分层:L1 进程、L2 本地磁盘(重启先加载上次快照)、L3 边缘/Relay,冷启动用镜像 bootstrap 或对象存储。LaunchDarkly persistent store 默认 15 秒来源;Splitd 把规则放宿主机内存,通过 Unix socket 给 thin SDK 求值,避免 PHP 每请求重建缓存来源。目标是缓存生命周期长于请求、短于配置版本。

曝光上报:业务真正读到 Flag 并决定展示时记,不在初始化时记;字段包括 experiment_id/version、flag_version、unit_id_hash、variant、bucket、timestamp、assignment_source、脱敏 context、event_id;有界队列 + 批量 + gzip + 重试 + 磁盘 spill,队列满采样并打点;event_id 幂等,服务端按 (experiment_id, unit_id_hash, variant, date) 去重;SDK 计 exposure_emitted,服务端计 exposure_ingested,差异告警;不上传原始 PII。Splitd 默认 impressions 8192/1800 秒、events 8192/60 秒来源;LaunchDarkly Go SDK 10000/5 秒来源。行业共识是批量、有界、定期 flush。

OpenFeature 是 CNCF 孵化项目,提供厂商中立 API、Provider、Context、Hooks、Events、Tracking、OFREP来源。价值是应用只依赖 getBooleanValue 等标准 API,换供应商只换 Provider;Hooks 在 before/after/error/finally 插入曝光和日志来源;OFREP 统一远程求值来源。但它不是实验平台:不定义实验对象、分流比例、SRM、CUPED、曝光去重,Tracking 仍是 experimental。它适合 SDK 抽象层,不适合统计分析层;flagd 是无 UI、无持久化的参考实现,从文件/HTTP/K8s/gRPC 同步,暴露 gRPC stream 和 OFREP来源。自建应「OpenFeature Provider + 自研实验语义」分层,不要把实验逻辑塞进 Provider。

移动端放大了问题:代码不可热改,treatment 随二进制预埋;审核 1–7 天;版本碎片让同一实验在不同版本上是不同 treatment;ATT/SKAdNetwork 让归因窗口模糊。所以第一功能是版本门槛与兼容性管理:Firebase Remote Config 下发 minimum_version来源;Eppo 的 SemVer targeting 要求完整 major.minor.patch,28.5 不合法来源;格式不统一会静默不命中,直接制造 SRM。移动端有 installation ID、设备 ID、账号、会话四种单元,平台应提供单元注册表,建实验时强制声明随机化/分析/曝光三个单元并校验一致。生命周期:代码预埋 → 版本门槛 → 小流量 ramp → 等目标版本渗透率达标 → 开实验 → 曝光 → 指标窗口 → 清理 flag。发版日就开实验,会让「愿意升级的用户」污染样本。Firebase 单实验最长 90 天、同时 24 个、项目内 300 个来源;SKAdNetwork 只给聚合延迟回传,不能当无偏真值来源;崩溃/ANR 必须进护栏。

求值通常不是瓶颈,瓶颈是 JSON 解析、字符串拼接、内存分配、日志序列化。优化:ruleset 预编译、复用哈希 buffer、曝光异步批量、缓存请求内重复求值、控制配置体积(Cloudflare Flagship 单 app 25MB、单 variant 10KB来源)。边缘求值受四个限制:上下文不足、日志按地区/网络非随机丢失(直接 SRM)、多节点版本不一致、绑定 CDN/Worker。SDK 验收标准不是语言数量,而是跨端同一用户同一 variant、曝光可对账;边缘只用于 flag 和低上下文,曝光回中心;30 秒传播窗口内不开新实验、不判胜负。

7.7 多租户、权限、审批、审计#

治理是把「谁能改、谁批、改了什么、何时生效」变成不可变事件。四级模型 tenant → project → environment → object;tenant_id 出现在所有表/消息/指标/日志,不靠分库隐式隔离。隔离三种:Pool(共享集群 + 行级安全 + 查询隔离,成本最低)、Silo(独立 DB/集群,强合规/超大租户)、Bridge(控制面共享,数据面/分析面在客户 VPC)。配额覆盖 Flag 数、实验数、QPS/SDK key、事件/天、并发分析任务、存储、审批人数;边缘 token bucket 返回 429 + Retry-After,分析面每租户队列 + 公平调度。

私有化难点是可运维:依赖矩阵、Helm/Operator、离线镜像/air-gap、离线签名 License(不 phone home,到期只读不锁死)、N-1 升级、可回滚 migration、自带 dashboard、元数据导出且曝光不回传。Unleash 有 cloud/hybrid/self-hosted,hybrid 是控制面在 Unleash、Edge 在客户侧来源;GrowthBook 单 Docker + MongoDB来源;LaunchDarkly Relay 形成「SaaS 控制面 + 私有数据面」来源。数据驻留:租户选 region,元数据/曝光/分析结果留在该区;全局控制面只存路由和计费;每租户 KMS key;跨区复制可关闭;审计可导出 SIEM;删除租户做硬删除/加密擦除。

RBAC 三层:根级(用户/角色/SSO/Token/集成)、项目级(Flag/Experiment 创建/编辑/归档/移动)、环境级(开关/策略/变体/审批/应用/跳过)。Unleash 支持自定义根/项目角色、环境权限、用户组、SSO 同步来源。关键:「能改实验」与「能看结果」必须分开,否则可以一边改分流一边看未锁定结果,诱导 peek 和 p-hacking。审批是状态机:变更集(一次审批覆盖多个对象/字段)、draft → in review → approved → scheduled → applied、评审中修改撤销批准、多人规则(Unleash 最多 10 人)、定时应用 + 冲突挂起、break-glass 只作用于环境级且留审计来源;LaunchDarkly approvals 拆成请求/评审/设置/环境配置来源。

审计事件含 event_id、tenant/project/env、actor、action、resource、before、after、request_id、ip、approval_id、timestamp。Unleash Event Log 保留 data 与 preData,可过滤/导出 CSV/JSON来源。建议加哈希链、SIEM 导出,保留 1–3 年;决策记录与审计分开存。版本化四条件:不可变、可寻址、可比较、可回滚(回滚 = 旧内容发新版)。Kill Switch p99 <60 秒、配置回滚 p99 <5 分钟,靠指针切换 + 边缘增量推送,不靠重新编译来源。Flag 债务:Fowler 分 release/experiment/ops/permission 四类来源;平台应强制 owner/类型/过期时间,超期标 stale,联动 code references,自动清理 PR,CI 阻断未登记 Flag,项目技术债评分;Unleash 已产品化来源。治理带宽守恒:官方路径越慢,shadow experiment 越多。

7.8 规模、可用性与可观测性#

先把数算清楚。10 万 QPS = 8.64 B 次求值/天;若 SDK 本地求值,中心只承担配置分发;20 万 SDK 按 30 秒轮询,配置请求约 6.7 k QPS。10 万实验 × 50 KB 元数据约 5 GB,10 个版本约 50 GB;10 万实验 × 10 变体 = 100 万变体,需要区间查找和编译快照。10 亿事件/天平均 11.6 k/s、峰值按 3× 约 35 k/s;200 B/事件 = 200 GB/天原始、约 20 GB/天 Parquet,90 天约 1.8 TB。以上为推断。

规模问题 常见误判 正确做法
10 万 QPS 把中心做成高并发 RPC 服务 SDK 本地求值;中心只发配置;边缘按 1 万–5 万 RPS/实例规划,N+1
100 万长连接 只算 QPS 不算连接 每实例 5 万–10 万连接,处理 fd、心跳、TLS 复用、优雅重启
10 万实验 以为元数据是瓶颈 元数据只有几 GB;瓶颈是快照大小、索引、调度与治理
10 亿事件/天 一上来就上 Flink Kafka + 对象存储 + Spark/ClickHouse 足够;只有护栏/实时看板走流
查询爆炸 按事件量估容量 实验×指标×维度组合远大于事件量;预聚合与查询配额才是关键

10 万实验的真正挑战不是存储,而是:快照大小(每个 SDK 都加载全部会到几百 MB,必须按 project/environment/flag set 分区,单个 SDK 只加载几百到几千个 Flag)、索引结构(flag_key 哈希 + 规则区间表,不要线性扫描)、实验调度(运行中每日增量、结束按需重算、归档只留摘要)、治理(几万个 owner、几千个指标,没有审批/TTL/搜索/标签会被元数据淹没)。字节 DataTester 在 2023 年 6 月披露累计 240 万次实验、日新增 4000 个、同时运行 5 万个来源(第三方报道,引用厂商数据);「累计」与「同时运行」是两个量级,后者对调度和快照的要求高得多。瓶颈排序:配置快照分发与冷启动 → 分析查询爆炸 → 曝光数据质量 → 控制面写冲突与审批体验 → 求值 QPS(只要本地化,反而最不是问题)。

SLI 定义 建议 SLO
求值可用性 成功求值/总求值 99.99%
本地/边缘求值延迟 p99 < 1 ms / < 10 ms
配置传播 commit → 99% 边缘可见 p99 < 5 s
曝光丢失率 1 - ingested/emitted < 0.1%
分析新鲜度 事件到达 → 报表可用 p95 < 30 min
控制 API 可用性/p99 99.9% / < 300 ms
回滚 MTTR 点击 → 全球生效 p99 < 60 s

99.99% 的可用性意味着每月约 4.3 分钟不可用;99.9% 是 43 分钟来源。不要给分析面定 99.99%,它可以用重跑换可用性;也不要给控制面定 99.99% 却不给数据库多活预算。观测要区分:指标看求值 QPS/错误/延迟、缓存命中、配置版本年龄、连接数、曝光队列深度/丢弃、Kafka lag、SRM 告警、护栏触发、审批时长;追踪只对配置拉取、边缘回源、分析任务打 span,不要对每次求值打 span;experiment_id/flag_key 放日志和 exemplar,不放 Prometheus label。

混沌演练覆盖:边缘被杀(关 10% 实例,p99 < 2×)、区域断连(本地快照继续服务)、缓存过期(singleflight 生效)、快照损坏(SDK 拒绝更新)、时钟漂移(版本仍单调)、Kafka broker 丢失(生产不丢)、热租户(配额生效)、惊群(本地快照 + 退避)、坏配置灰度(自动回滚)、高峰回滚(< 60 秒)。原则是定义稳态、注入真实事件、对照组证伪、最小爆炸半径、自动化来源;AWS FIS 用 action/target/stop condition + CloudWatch 告警停止来源。规模的敌人不是求值 QPS,而是快照分发、查询组合爆炸、曝光质量;SLO 要按平面分配。

7.9 组织、流程与合规#

先承认:多数实验不会赢。美团 2025 可信实验白皮书引用 Google 与 Microsoft 的统计:即使很有经验的人,对产品策略判断正确的概率也只有约三分之一来源。Spotify 自报实验胜率约 12%、学习率约 64%来源;据第三方分析,Netflix 也只有很小比例的被测想法最终上线来源。如果考核胜率,理性团队就会只挑容易赢的实验、事后改口径,或干脆不登记。更可取的公式是:实验能力 = 可信度 × 决策覆盖率 × 决策速度 ÷ 单位实验成本;平台只能直接改善可信度与单位成本,覆盖率取决于流程是否够轻,速度取决于谁有权拍板。

成熟度可分 L0 无实验、L1 个人 ad hoc、L2 团队标准化、L3 平台化治理、L4 组合级;多数公司卡在 L1–L2,L3 的硬信号是预注册率、SRM 硬阻断、核心指标唯一 owner。

Fabijan 等人基于 Microsoft/Booking/Skyscanner/Intuit 的四阶段模型覆盖七个方面来源;公开摘要没给每阶段完整命名,这里不臆造。据第三方转述,Booking 达到约 1000 个并行实验后,早期质量仪表盘发现 80% 实验没做功效计算来源——规模化本身制造质量债务。

组织形态没有标准答案,但成功案例都把「证据生产」与「风险承担」分开。Netflix 是 hub-and-spoke + Informed Captain:中央 XP 管 assignment/下发/join/自动分析,领域 DS 管假设与解释,重大决策由单一 Captain 拍板来源。Booking 是分散决策 + 中央机器 + 大使网络:产品团队端到端拥有实验与决策,中央管平台/标准/质量/safeguard来源。Google 2010 年 Search 是自助 + Experiment Council + 开放论坛来源;Spotify 是中央平台 + 自助 + Surface,exposure = Config Assigned 且 Applied来源。

建议三层治理:中央平台层管分流/曝光 SDK、指标语义、统计、SRM/AA、护栏、权限审计、实验库,不拥有业务假设;领域实验层由产品负责人拥有 hypothesis 与最终决策,DS 管设计完整性,工程管 assignment/exposure 质量;治理层只在高风险/跨域冲突时介入,指标委员会管定义与认证,法务管合规边界。评审按风险分级:

级别 触发条件 评审者 SLA 决策权
Tier 0 自动 低风险 UI/文案;流量 <5%;无定价/隐私/资金 平台自动检查 分钟级 自动放行,事后抽检
Tier 1 同伴 单团队、单 surface、可逆 团队内资深 DS/PM 1 个工作日 建议,不阻断
Tier 2 委员会 跨团队 surface;>20% 流量;核心指标 评审委员会 + 领域 DS 3 个工作日 可要求整改/补功效/加护栏
Tier 3 高管/合规 定价、资金、青少年、敏感数据、跨境、不可逆 委员会 + 法务 + 业务负责人 5–10 个工作日 否决或附条件放行

关键:默认不审、例外才审(Tier 0 占比低于 70% 说明分级失效);评审设计而非结果;SLA 与升级写进章程;保留异议记录。Confidence Reviews 支持按 surface 配必审人,审批后改 variant/audience 重置审批来源。

预注册不是写 H0/H1,而是和真正的决策者写下「如果看到 X,就做 Y」。Eppo 指出 5 个指标×3 种状态=243 种结果组合,事先覆盖最可能的几种,明确 null、混合、护栏触碰时怎么办来源;关键点:不太可能改变决策就别做;null 也要预注册动作;必须找到真正的决策者;允许非数据考量显性化。Eppo Experiment Protocol 把 entity、日志表、统计方法、默认时长、primary/guardrail、推荐决策、最大指标数预设进模板,偏离要写理由来源。

信任是链:分配→曝光→指标→分析→决策。AA 验证 SDK 集成、分流、指标定义,建议新 SDK/数仓/代码变更后跑来源;AA 通过不代表真实实验不会 SRM。SRM 红灯不读指标、不做决策;规则要进平台,红灯锁「Make Decision」。Statsig 阈值:p 0.001–0.01 黄色,p<0.001 且偏差≥0.1% 红色来源;Eppo guardrail cutoff 用置信区间下界触碰即黄色、显著为负红色来源;Statsig Safeguards 可自动 pause/rollback/complete来源(2025-09-30 仍标 open beta,待核实)。这里存在不同说法:开源 SRM Checker 默认 p<0.01 告警,Statsig 把 p<0.001 且偏差≥0.1% 定红;阈值应与实验量/误报成本匹配。

合规把实验当数据处理活动:GDPR 要求合法/公平/透明/目的限制/最小化/存储限制/问责(Art 5)来源,合法性基础含同意/合同/法定义务/重大利益/公共任务/正当利益(Art 6),Art 22 自动化决策可拒绝/要求人工,跨境走充分性/SCC/BCR/例外(Art 44–49),最高 2000 万欧元或全球营业额 4%。CCPA/CPRA 适用年收入 >2500 万美元、年买卖/共享 10 万以上加州消费者/家庭、或 >50% 收入来自出售 PI 的企业来源;每次违规最高 2500/7500 美元。PIPL 要求合法正当必要诚信(Art 13),敏感/对外提供/跨境/公开单独同意(Art 14/23/29/39),最短保留(Art 19),自动化决策不得不合理差别待遇(Art 24),跨境走评估/认证/标准合同(Art 38–40),最高 5000 万元或上一年营业额 5%。

中国区特别约束:实验通常不属「合同必需」,PIPL 又无 GDPR 式正当利益,需谨慎落到同意或其他法定情形;跨境按 2024-03-22《促进和规范数据跨境流动规定》:非 CIIO 年出境 <10 万人(非敏感)豁免;10–100 万或 <1 万敏感走标准合同/认证;≥100 万或 ≥1 万敏感走安全评估来源。个人信息保护合规审计 2025-05-01 施行,处理 >1000 万人 PI 每两年至少审计一次来源;算法推荐规定要求关闭选项、可删除个人特征标签、禁止交易价格等不合理差别待遇来源。个性化定价实验风险极高,可能被认定「大数据杀熟」;不做个人差别定价,或只在有合法依据、可解释、可申诉范围内做,并提前法务评估(推断)。

AI/LLM 实验把 treatment 从「代码分支」变成「概率分布」:模型+prompt 版本+temperature/top_p+检索库+工具链+安全策略,任何一项变了都是新 treatment;记录必须绑定模型版本、prompt hash、参数、索引版本。arXiv 2407.10457 指出 LLM 评估常忽略非确定性,只测一个输出,greedy 与 sampling 差异显著来源;同一 treatment 重复输出有方差,MDE 更大,缓解是每单元多次采样,但成本线性上升。LLM-as-judge 有位置/冗长/自我增强偏差,强 judge 与人类偏好 80%+ 一致但仍需校准来源;judge 不是真值。用 LLM 代替人类做 A/B 更危险:替代终点框架下原始 LLM 输出只能恢复 39% 的人类处理效应来源,所以 LLM 实验只能做前置筛选,上线仍要真实用户 A/B。流程:离线 eval 门禁→shadow→在线 A/B→人工评审;成本看每成功任务/每用户/重试兜底。

7.10 模块级 build vs buy#

不要按「平台」整体决策,而要按模块决策。分界线是语义是否属于你:实验对象模型、分流语义、曝光口径、统计方法、指标治理,是差异化且与业务数据强耦合,适合自建;Flag 求值引擎、配置分发、边缘缓存、多语言 SDK、可观测性,是商品化能力,适合采购或复用开源;指标计算、实验分析优先用现有湖仓,不另建分析数据库。

模块 自建理由 采购/OSS 理由 推荐 优先级
实验元数据 对象模型是核心资产 通用工作流可复用 自建 P0
工作流/审批 流程与组织强相关 企业 BPM 可复用 自建或复用 P0
RBAC/SSO 合规要求 企业 IAM 已具备 复用 P0
配置分发 特殊网络/合规 Relay/Edge 已解决多区域 采购/OSS P0
SDK 需要业务上下文 多语言维护成本极高 OpenFeature + 薄层 P0
曝光管道 口径与数仓强耦合 Kafka/Flink 通用 自建 P0
指标/统计 差异化核心 Eppo/Statsig/GrowthBook 可参考 自建 P1
分析计算 已有湖仓 无需重复建设 仓原生 P1
报表 UI 实验工作流特殊 BI 工具可嵌入 自建 P1
可观测性 无 OTel/Prometheus/Grafana 成熟 采购/OSS P1

三套组合:A 采购 Flag + 自建实验层(LaunchDarkly/Unleash 做 Flag 求值和分发,自建实验元数据、曝光、统计;上线快,但两套控制面要 OpenFeature/双写过渡);B 开源自建一体化(GrowthBook/Unleash + 自建分析;可控、私有化,但升级/SDK 兼容/边缘运维自担);C 全自研(控制面、数据面、分析面全自建,SDK 用 OpenFeature,配置分发复用 Envoy/Redis/CDN;深度定制但投入巨大)。团队规模存在不同说法:架构调研按全栈自建估 15–25 人;路线图分册估 MVP 7 人、规模化 12–15 人、成熟 18–25 人;选型分册给 6–10 人核心团队(复用 OSS/云服务)。差异来自是否包含 SDK/边缘/SRE/私有化,预算前先对齐口径。

路线 控制面 数据面 分析面 强项 代价/限制 适用
Flag-first SaaS(LaunchDarkly/Split) 成熟、审批/RBAC 强 SDK+Relay/Edge LaunchDarkly 原生统计完整(频率派/贝叶斯/序贯/CUPED/SRM);Split/Harness FME 偏实验编排 交付安全、生态、多语言 统计深度因产品而异;成本随 seats/MAU 研发驱动、Flag 先行
Experiment-first SaaS(Statsig/Eppo/Amplitude) 实验/Flag 一体 全球边缘、仓库原生 统计强、报表快 实验效率、贝叶斯/序贯 数据出域/合规;定制受限 增长/产品实验密集
开源自建(Unleash/GrowthBook/Flagsmith) 可自托管、权限/审批 SDK+Edge/Proxy GrowthBook 自带统计,其他需外接 成本可控、私有化 运维/升级/SDK 兼容自担 有平台团队、私有化
大厂自建(Google/Microsoft/Netflix/ByteDance/Meituan) 深度定制、分层/域 自研 SDK/边缘 自研指标/统计/湖仓 规模、统计深度、组织贴合 投入巨大、复用难 实验是核心
云厂商(DataTester/Firebase) 托管、开箱即用 厂商边缘/SDK 托管分析 上线快、与云集成 锁定、跨境/合规、定制弱 用该云、实验不是核心

供应商稳定性必须进入选型。Google Optimize 于 2023-09-30 停服来源;AWS Evidently 于 2025-10-17 终止来源;Split 被 Harness 收购(官方称 2024-06-11 完成交割,DLA Piper 公告口径为 2024-07,存在不同说法,以官方交割日为准)来源;Eppo 于 2025-05-05 被 Datadog 收购来源;Statsig 于 2025-09-02 宣布加入 OpenAI,2026-05-05 产品/品牌/客户转入 Amplitude,原团队留 OpenAI来源来源;Optimizely 于 2024-09-30 收购 NetSpring来源;Unleash Edge OSS 的 LTS 到 2025-12-12、EOL 为 2026-12-31来源。合同必须写清:配置/曝光历史/指标定义可导出(尤其 assignment 级曝光日志,只有聚合报表无法复算历史)、迁移协助期、子处理商变更通知、价格保护、停服/被收购时的数据保留与删除证明、escrow。

什么时候不该自建:实验 <100/年、团队 <3 人,SaaS 更划算;没有数仓/埋点,先补数据基础;合规不允许出境又不愿私有化运维,先混合架构;只做灰度发布,Flag 平台足够。务实的组合是:控制面与实验语义自建,Flag 求值/配置分发用 OSS 或采购,SDK 用 OpenFeature 薄层,指标计算仓原生,可观测性采购。落地 90 天对象模型/SDK 本地求值/曝光入 Kafka/SRM/审批审计,180 天边缘多区域/CUPED/指标平台/版本化,12 个月私有化/多租户/混沌/自动回滚。自建的是语义,采购的是通道;语义一旦外包,实验平台就退化成报表工具。

NOTE本页内容来自《AB 测试平台全网竞品与方案调研》2026-10 版。价格、融资与产品状态以文中标注日期为准;采购前请以供应商最新报价与合同为准。