A/B 测试实验平台:独立站转化率优化
用 Node.js + PostgreSQL 构建轻量 A/B 测试平台,支持多变量同时实验、实时统计显著性检验、置信区间自动计算,服务于 3 个独立站日均 5 万次 UV 的转化率优化。
项目背景
独立站运营的日常困惑:按钮颜色改蓝色还是红色?产品描述放前面还是后面?每个改动都有人说服力,但谁也说不准。背后缺的是数据驱动的实验文化——用 A/B 测试量化每个改动的真实影响。
本系统构建了一个内部 A/B 测试平台,支持多变量同时跑实验、实时显著性检验、自动停机和邮件告警。目前服务于 3 个独立站,日均处理 5 万 UV 的实验分流,日均运行 12 组并发实验。
技术架构
架构分三层:分流层(Node.js Middleware 读取用户 Cookie 中已分配的实验 ID,新用户按哈希均匀分流)+ 数据层(PostgreSQL 存储实验配置、用户分配记录、每小时的转化事件)+ 分析层(每 10 分钟跑一次统计检验,计算 p 值和置信区间)。
const { createClient } = require('@supabase/supabase-js')
const crypto = require('crypto')
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY)
function hashUser(userId, experimentId) {
return crypto.createHash('sha256').update(`${userId}:${experimentId}`).digest('hex')
}
function assignVariant(userId, experimentId, variants) {
const hash = hashUser(userId, experimentId)
const bucket = parseInt(hash.substring(0, 8), 16) % 100
let cumulative = 0
for (let i = 0; i < variants.length; i++) {
cumulative += variants[i].weight
if (bucket < cumulative) return variants[i].name
}
return variants[variants.length - 1].name
}
function getOrCreateUser(userId, experimentId) {
const variant = assignVariant(userId, experimentId, [
{ name: 'control', weight: 50 },
{ name: 'variant_a', weight: 50 },
])
return { userId, experimentId, variant, bucket: parseInt(hashUser(userId, experimentId).substring(0, 8), 16) % 100 }
}
const user = getOrCreateUser('user_12345', 'exp_checkout_btn_color')
console.log('Assigned:', user)核心代码
统计显著性用 Bayesian 方法(Beta 分布)计算,比 frequentist 的 p 值更直观——直接给出" variant 优于 control 的概率是多少"。转化率数据每 10 分钟聚合一次,低于设定阈值自动告警。
import math
from scipy.stats import beta
def bayes_ctr_confidence(control_visits, control_convs, variant_visits, variant_convs, alpha=0.05):
control_rate = control_convs / control_visits if control_visits > 0 else 0
variant_rate = variant_convs / variant_visits if variant_vis > 0> 0 else 0
# Beta posterior samples
control_samples = beta.rvs(control_convs + 1, control_visits - control_convs + 1, size=10000)
variant_samples = beta.rvs(variant_convs + 1, variant_visits - variant_convs + 1, size=10000)
# Probability variant > control
prob = (variant_samp > c> control_samples).mean()
# Credible interval for variant
ci_low, ci_high = beta.ppf(alpha/2, variant_convs+1, variant_visits-variant_convs+1), \
beta.ppf(1-alpha/2, variant_convs+1, variant_visits-variant_convs+1)
return {'prob_better': prob, 'variant_rate': variant_rate, 'ci_95': (ci_low, ci_high)}
result = bayes_ctr_confidence(10000, 320, 10000, 385)
print(f"Variant is {result['prob_better']*100:.1f}% likely to be better")
print(f"Variant CTR: {result['variant_rate']*100:.2f}%, 95% CI: [{result['ci_95'][0]*100:.2f}%, {result['ci_95'][1]*100:.2f}%]")关键指标
· 分流均匀度:Chi-square test p > 0.05,各实验组访问量偏差 < 5%
· 决策速度:达到 95% 置信度平均需要 7 天(视转化率样本量而定)
· 误判率:历史实验回测,错误停机率(实验无真实效果但被判定有效)< 5%
· 支持并发实验:最多同时跑 20 组实验,互不干扰
最小样本量:转化率实验的最小样本量取决于基线转化率和最小可检测效应(MDE)。基线 3%、MDE 20% 时,每组至少需要 15000 UV 才能有足够的统计功效。别在样本量不足时提前决策。