- lucky_gift_experiments/overrides 表(006 迁移,含分组统计索引与 binary user_key) - 创建实验=同事务发布 treatment+登记;两组强制同策略;实验期常规发布拒绝 - Check/单抽/批量/外部抽按用户钉住组内版本;结束实验重发布获胜快照全量收敛 - AdminLuckyGiftService 新增 5 个实验 RPC(放量字段 proto3 optional) - admin 实验管理 HTTP API + lucky-gift:experiment 权限(迁移 103) - 设计文档 docs/幸运礼物AB实验.md;域/真库/admin 回归测试 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
54 lines
4.4 KiB
SQL
54 lines
4.4 KiB
SQL
SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||
|
||
USE hyapp_lucky_gift;
|
||
|
||
-- AB 实验事实表:两组各钉住一个不可变 rule_version,运行时按 bucket_salt + user 哈希分流。
|
||
-- active 生成列只在未完成状态为 1,配合唯一键保证同一奖池同时最多一个未完成实验;
|
||
-- completed 后 active 为 NULL,不占用唯一键,历史实验可以保留任意多条。
|
||
CREATE TABLE IF NOT EXISTS lucky_gift_experiments (
|
||
app_code VARCHAR(32) NOT NULL COMMENT '应用编码',
|
||
experiment_id VARCHAR(64) NOT NULL COMMENT '实验 ID',
|
||
pool_id VARCHAR(96) NOT NULL COMMENT '幸运礼物奖池 ID',
|
||
name VARCHAR(128) NOT NULL COMMENT '实验名称',
|
||
control_rule_version BIGINT NOT NULL COMMENT '对照组钉住的规则版本',
|
||
treatment_rule_version BIGINT NOT NULL COMMENT '实验组钉住的规则版本',
|
||
treatment_traffic_ppm BIGINT NOT NULL DEFAULT 0 COMMENT '实验组流量比例 ppm,0..1000000',
|
||
bucket_salt VARCHAR(64) NOT NULL COMMENT '分桶盐;实验生命周期内不可变,保证同一用户分组稳定',
|
||
status VARCHAR(16) NOT NULL DEFAULT 'running' COMMENT 'running/paused/completed',
|
||
active TINYINT AS (IF(status IN ('running', 'paused'), 1, NULL)) STORED COMMENT '未完成实验为 1,completed 为 NULL',
|
||
winner_arm VARCHAR(16) NOT NULL DEFAULT '' COMMENT '结束实验时选择的获胜组 control/treatment',
|
||
winner_rule_version BIGINT NOT NULL DEFAULT 0 COMMENT '获胜组钉住的规则版本',
|
||
final_rule_version BIGINT NOT NULL DEFAULT 0 COMMENT '结束时重发布的收敛版本,全量流量由它承接',
|
||
created_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '创建管理员 ID',
|
||
started_at_ms BIGINT NOT NULL COMMENT '实验开始时间,UTC epoch ms',
|
||
ended_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '实验结束时间,UTC epoch ms',
|
||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||
PRIMARY KEY (app_code, experiment_id),
|
||
UNIQUE KEY uk_lucky_gift_experiments_active_pool (app_code, pool_id, active),
|
||
KEY idx_lucky_gift_experiments_pool (app_code, pool_id, started_at_ms)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='幸运礼物 AB 实验';
|
||
|
||
-- 白名单 override:把指定用户强制固定到某个组,用于放量前验证。
|
||
-- user_key:内部用户为十进制 user_id;外部用户为 "ext:" + external_user_id。
|
||
CREATE TABLE IF NOT EXISTS lucky_gift_experiment_overrides (
|
||
app_code VARCHAR(32) NOT NULL COMMENT '应用编码',
|
||
experiment_id VARCHAR(64) NOT NULL COMMENT '实验 ID',
|
||
-- user_key 必须字节精确(binary collation):FNV 分桶按字节计算,ci 排序规则会让大小写不同的外部用户在白名单上碰撞/静默失配。
|
||
user_key VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '用户标识:内部十进制 user_id 或 ext:external_user_id',
|
||
arm VARCHAR(16) NOT NULL COMMENT '强制组 control/treatment',
|
||
created_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '操作管理员 ID',
|
||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||
PRIMARY KEY (app_code, experiment_id, user_key)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='幸运礼物 AB 实验用户白名单';
|
||
|
||
-- 实验分组统计按 (app, pool, rule_version, 时间窗) 聚合;没有含 rule_version 的索引时热池会退化为大范围回表扫描。
|
||
SET @ddl := IF((SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'lucky_draw_records' AND INDEX_NAME = 'idx_lucky_draw_pool_version') = 0,
|
||
'ALTER TABLE lucky_draw_records ADD KEY idx_lucky_draw_pool_version (app_code, pool_id, rule_version, created_at_ms)', 'SELECT 1');
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
|
||
-- 兜底:006 首版曾以表默认 ci 排序规则建过 overrides 表;重复执行时归一到 binary。
|
||
SET @ddl := IF((SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'lucky_gift_experiment_overrides' AND COLUMN_NAME = 'user_key' AND COLLATION_NAME <> 'utf8mb4_bin') = 1,
|
||
'ALTER TABLE lucky_gift_experiment_overrides MODIFY user_key VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT ''用户标识:内部十进制 user_id 或 ext:external_user_id''', 'SELECT 1');
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|