自定义指标存储选型与压测报告
选型:D 元信息建表,即定义表、例外取值表与上报范围、范围标签、上报成员三张新表,已有表零 DDL。 在 MySQL 5.7.44 上对四个完整方案端到端压测,只有 D 同时满足四项功能且迁移失败时存量功能不受影响。B 按服务拆行的最大数据源管理页首页 28.6 s、上界规模新增 42.1 GiB,C 元信息加列读写与 D 同量级,但加 5 列整表重建 68.4 s,迁移失败时 5 项存量功能中 4 项报 1054。
9.2 上的单项结论在 5.7 上大多成立,改变的有两处:同一画像下服务视图不再区分展开与窄成员表,定义表逐行关联时整组读取与全平台扫描比扩列慢 60%、76%,原生 arm64 构建上为 49%、38%。按范围不同的取值,定义行让「指标默认值」从对成员计票的 709 ms~940 ms 降到 66 ms,例外引用按指纹去重的取值,体积不随例外比例与全体改值膨胀。
压测数据按平台 1,000,000 个指标项全量构造,画像取自上云环境(bkte)的抽样。完整方案对比在 Docker 的 MySQL 5.7.44(amd64 模拟)上运行,单项选型压测在本机 MySQL 9.2.0 上运行,两者缓冲池都容纳全部热数据,结论看相对差。版本兼容在 MySQL 5.7.44、8.0.46 上验证。
0x01 结论
| 问题 | 结论 | 依据 |
|---|---|---|
| 完整方案 | D 元信息建表 | 5.7 上四项功能全部满足,已有表零 DDL,迁移失败时存量功能 0/5 报错 |
| 服务是否进入指标项身份 | 否 | 5.7 上最大数据源管理页首页 28.6 s,上界规模新增 42.1 GiB,9.2 上首页 1,133 ms |
| 是否分表 | 否 | 分 16 张表后最大数据源管理页首页 1,387 ms,热点数据源整组落在一张分表 |
| 上报关系存储 | 窄成员表 | 上界规模成员 6,749 MiB,5.7 上展开方案的最大数据源管理页首页慢 530 倍 |
| 元信息落点 | 独立定义表 | [a] 已有表零 DDL,迁移失败不影响存量功能 [b] 5.7 上整组读取与全平台扫描比扩列慢 38%~76% |
| 按范围不同的取值 | 定义加例外引用 | 指标默认值 66 ms,对成员计票的存法 709 ms~940 ms,上界规模体积 5.8 GiB~6.2 GiB |
| 成员回收 | 唯一键分段扫描,不建 last_seen 索引 | 最大数据源单组 2.75 s,上界规模省 1,123 MiB |
| 写入 SQL | 先查后插,必须 upsert 时用 VALUES() | [a] 5.7 不支持行别名[b] upsert 每批消耗与批大小相同的自增 ID |
| 索引文本列字符集 | utf8 | utf8mb4 的 varchar(255) 在 COMPACT 行格式下报 1071 |
__scope 表示 | 哈希唯一键加标签倒排 | 1,000 个服务时按服务过滤 p50 0.070 ms |
| 主键形态 | Django 自增主键加唯一键 | 成员表比复合主键大 14.6% |
- [1] 平台指标项总数与生产硬件「待确认」,上线前在生产 MySQL
5.7上复跑 validation 的make plans57 plans57-supplement plans57-supplement2 plans57-full。
0x02 完整方案对比(MySQL 5.7)
四个完整方案在 MySQL 5.7.44 上用同一份数据跑同一组用例,D 是唯一同时满足四项功能、已有表零 DDL、迁移失败时存量功能不受影响的方案。B 的最大数据源管理页首页 28.6 s、上界规模新增 42.1 GiB,在 5.7 上比 9.2 更不可行。C 的读写与 D 同量级,但加 5 列要整表重建,迁移失败时 5 项存量功能中 4 项报 1054。C、D 的关系表装满上界规模 4,009 万行后,读写耗时与 232 万行时相差在 11% 以内。
a. 总表
| 维度 | A 沿用白名单 | B 按服务拆行 | C 元信息加列 | D 元信息建表(选定) |
|---|---|---|---|---|
| 移入自建分组后服务视图 | 不可见 | 可见 | 可见 | 可见 |
| 按范围的例外 | 不能表达 | 每行自带元信息 | 引用例外取值 | 引用例外取值 |
| 元信息先于指标同步到达 | 不能落库 | 不能落库 | 不能落库 | 可以落库 |
| Histogram 按原名组族 | 不能 | 可以 | 可以 | 可以 |
| 新增体积,活跃规模 [2] | 0 MiB | 10,270 MiB | 2,151 MiB | 2,288 MiB |
| 新增体积,上界规模 | 0 MiB | 43,065 MiB | 7,697 MiB | 7,833 MiB |
| 最大数据源管理页首页 [3] | 35.0 ms | 28.6 s [4] | 42.3 ms | 37.1 ms |
| 最大数据源管理页计数 | 34.2 ms | 4,128 ms | 39.6 ms | 34.6 ms |
最大数据源 OFFSET 10000 | 183 ms | 30.0 s | 204 ms | 232 ms [5] |
| 最大数据源管理页搜索 [12] | 19.3 ms | 无 | 227 ms,C2 20.4 ms | 372 ms [13] |
| 最大数据源族视图节点分页 | 40.2 ms | 无 | 134 ms,C2 61.9 ms | 208 ms [14] |
| 最大数据源族视图首页完整步骤 [16] | 36.4 ms | 无 | C2 60.6 ms | D2 96.2 ms |
| 服务视图成员查询,最大数据源 | Redis 白名单 [6] | 20.0 ms [17] | 20.9 ms | 27.7 ms |
服务视图成员查询,126 个服务的数据源 | Redis 白名单 | 2.50 ms | 2.69 ms | 3.48 ms |
| 服务视图完整路径,最大数据源 [18] | 114 ms | 无 | C2 156 ms | D2 231 ms |
单范围读取,最大的 10 个范围 [15] | 不涉及 | 无 | 33.2 ms | 33.4 ms |
| 按指标读取元信息,最大数据源整组 | 112 ms | 7,539 ms | 148 ms | 236 ms [5] |
| 全平台扫描,活跃规模 | 4.9 s | 72.0 s [7] | 6.8 s | 11.9 s [5] |
bmw 每批 1,000 条上报 p50 [8] | 不涉及 | 22.6 ms | 34.4 ms | 29.1 ms |
| 每日刷新,最大数据源 [9] | 不涉及 | 1,810 s | 34.9 s | 29.5 s |
| 已有表 DDL [10] | 无 | 整表重建 68.4 s,Django 逐列 128.5 s [11] | 整表重建 68.4 s,Django 逐列 128.5 s | 只建五张表 0.12 s |
| DDL 期间读写最长停顿 | 无 | 618 ms,Django 逐列 1,485 ms | 618 ms,Django 逐列 1,485 ms | 3.5 ms |
3 s 长事务期间执行 DDL | 不涉及 | 读写排队 2.7 s | 读写排队 2.7 s | 不排队 |
| 迁移失败 | 不涉及 | 存量 4/5 报 1054 | 存量 4/5 报 1054 | 存量 0/5,新功能报 1146 |
| 回退 | 改回代码 | 收拢数据并删列重建,活跃规模约 397 s | 删列重建 24.5 s,Django 逐列 176.1 s | 删五张表 0.12 s |
field_id 与已保存视图 | 保持 | 展开行新增 field_id | 保持 | 保持 |
| 旧代码读改造后数据 | 正常 | 管理页查不到已展开的指标 | 正常 | 正常 |
- [1] 读取为 TCP 上的服务端执行与结果集读取,不做类型转换,环境与规模见下表。
- [2] 体积按实测每行字节外推,B 用展开表替换原有指标项,新增量已扣除原表
678 MiB。 - [3] A 不改表,管理页、计数、整组读取与全平台扫描即现状。
- [4] B 按指标名去重,单条分页语句
19.7 s,一次请求含计数、本页分组与分页共4条语句。 - [5] D 与指标项同一条语句关联定义,按 PLAN 单独读取定义时分别为
186 ms、221 ms、9.9 s。 - [6] 白名单单次扫描受
slimit 3000截断,最大应用触及 VM500,000条上限。 - [7] B 读取已装载的展开行,按每行耗时外推到活跃规模,上界规模为
288 s。 - [8] C、D 按 PLAN 把带范围的改值记为例外,按
9.2的写法改写定义时为37.5 ms、31.5 ms。 - [9] B 沿用 bmw 逐行 UPDATE,按抽样每行
1,830 µs乘984,828个活跃行,C、D 按 Apply 每千条 p50 乘1,014,511个成员换算。 - [10] DDL 与回退在
1,000,000行指标项副本上执行,期间每20 ms发一次管理页读与指标同步写。 - [11] B 加列后还要在热表上写入约
9,047,633行展开行,回退时再收拢。 - [12] 搜索、节点分页与单范围读取为补测,不装载 B 的展开行,B 的这三项都要按指标名去重或按服务拼
field_scope,管理页首页已达28.6 s。 - [13] A 按指标名搜索,C、D 沿用
9.2的写法按指标名、原名与描述匹配,按 PLAN 只匹配指标名时 C2 为20.4 ms,D 再按本页读定义为20.6 ms,先在定义表按描述查出指标名时为108 ms。 - [14] A 没有族,节点即指标,C2、D 按 PLAN「族在内存中组装」读整组指标项与原名,在客户端分页后补齐本页节点的族成员并读本页定义,D 为
93.5 ms。 - [15] 范围平均
6,003个成员,取例外引用写法,例外内联在成员上时 C、D 为31.5 ms、31.6 ms,先读成员再按 ID 读定义为42.2 ms。 - [16] 在节点分页之后再取本页页面分组名并解析有效配置,A 为同一轮测得的现状管理页首页。
- [17] B 没有成员表,为按服务拼
field_scope读展开行。 - [18] 读整组指标项与页面分组,D2 另读整组定义,再取该服务的成员在客户端过滤,A 只计整组读取,
126个服务的数据源为 A2.08 ms、C23.36 ms、D24.62 ms。
| 环境与规模 | 取值 |
|---|---|
| 数据库 | 官方镜像 mysql:5.7.44(amd64),在 Apple M4 Pro 的 OrbStack 中经 Rosetta 运行 |
| 参数 | 缓冲池 8 GiB,innodb_log_file_size=1G,innodb_flush_log_at_trx_commit=1,DYNAMIC 行格式,关闭 binlog |
| 指标项 | 按上界规模画像全量装载 1,000,000 个,分属 6,722 个数据源 |
| 成员与展开行 | 1 个最大数据源、60 个 126 服务的大型数据源与中小微型各 3%,共 2,322,252 个成员、2,324,834 行展开行 |
| 外推口径 | 体积按每行字节乘目标行数,B 的全平台扫描与回退按每行耗时乘展开行数 |
| 装载与磁盘 | 指标项、扩列与定义 159 s,成员、例外取值与展开行 215 s,数据目录峰值 8.71 GiB |
- [1] 活跃规模目标为
9,952,703个成员、72,379个范围、84,922个例外取值与10,047,633行展开行,上界规模依次为40,091,444、370,456、275,112与40,144,125。 - [2] 例外取值与 B 的无成员指标行按各范围的抽样概率求期望,成员与范围为精确计数。
- [3] amd64 模拟使绝对值偏慢,现状的最大数据源管理页首页从
9.2的15.4 ms变为35.0 ms,结论看相对差。 - [4] 复现命令为
make plans57,原始输出在results/plans57.txt,补测三项为make plans57-supplement,输出在results/plans57_supplement.txt,C2 与完整路径为make plans57-supplement2,输出在results/plans57_supplement2.txt。
b. 与 MySQL 9.2 的对照
| 9.2 的结论 | 9.2 依据 | 5.7 实测 | 5.7 下 |
|---|---|---|---|
| 服务不进入指标项身份 | B 管理页首页 1,133 ms,是窄成员表的 73 倍 | B 单条分页 19.7 s,是 D 的 530 倍 | 成立,差距扩大 |
| 展开方案体积大 | B 上界规模 30.5 GiB | B 上界规模新增 42.1 GiB | 成立 [1] |
| 展开方案全平台扫描最慢 | B 48.2 s,定义表 5.47 s | B 72.0 s,D 11.9 s | 成立 |
| 窄成员表的服务视图快于展开 | B 105 ms,定义表 13.5 ms | B 20.0 ms,D 27.7 ms | 改变 [2] |
| 定义表只在搜索与节点分页上慢于扩列 | 整组读取 C 104 ms、D 107 ms,扫描 C 5.51 s、D 5.47 s | 整组读取 C 148 ms、D 236 ms,扫描 C 6.8 s、D 11.9 s | 改变 [3] |
定义表的管理页首页增量不超过 2 ms | D 比现状多 0.2 ms | D 比现状多 2.1 ms,C 多 7.4 ms | D 接近上限,C 超出 |
| Apply 定义表最快 | C 24.2 ms,D 21.9 ms | C 37.5 ms,D 31.5 ms | 成立 [4] |
| 定义表方案总体积 | 上界规模 8,129 MiB | 上界规模 8,511 MiB | 成立 [5] |
5.7 加列整表重建 | 676 MiB 的表 37.5 s | 一条 ALTER 68.4 s,Django 逐列 128.5 s | 成立,代价更大 |
| 定义表的管理页搜索慢于扩列 | C 48.6 ms,D 104.5 ms | C 227 ms,D 372 ms | 按 9.2 写法成立,按 PLAN 写法持平 [6] |
| 定义表的族视图节点分页慢于扩列 | C 49.1 ms,D 120.3 ms | C 134 ms,D 208 ms | 成立,内存组装 D 为 93.5 ms [7] |
| 单范围读取例外引用慢于内联 | 每千成员 3.22 ms 对 1.94 ms | 每千成员 5.57 ms 对 5.27 ms | 改变 [8] |
- [1]
5.7的展开表每行多5列元信息,9.2的展开表不含元信息。 - [2] 改变来自画像而非版本,
9.2的展开表让最大数据源的50个服务各上报全部19,900个指标,本次与窄成员表共用667个服务的画像,单服务约1,500行。 - [3]
5.7上逐行按(group_id, field_name)关联定义更贵,单独读取定义后为221 ms、9.9 s,仍慢于扩列。 - [4] C、D 共用成员与范围,先跑的 C 替 D 刷新了一部分到期行,每批写入
115行对88行,差值偏大,9.2的执行顺序相同。 - [5] 含
1,000,000行指标项,成员每行183 B,另含例外取值表。 - [6] 现状搜索
9.2为23.4 ms、5.7为19.3 ms,C、D 变慢主要来自三条语句都对每个指标项求JSON_EXTRACT描述,只匹配指标名时 C220.4 ms、D20.6 ms。 - [7] 内存组装在原生构建上为
86.5 ms,慢于 C 联表的67.2 ms,C 同样在内存组装时为61.9 ms、原生构建56.2 ms,D 慢在多读一次整组定义。 - [8]
9.2的单范围用例把四种存法同库装载,带例外取值表的存法受缓存竞争影响,5.7上两种写法只差6%,C 的对应写法也差6%。
执行计划的差异集中在三处,只有 OFFSET 10000 的改道同时影响 A、C、D,不改变方案间的排序:
- B 的管理页按指标名分组时带
tag_list(LONGTEXT)与 JSON 列,5.7的内部临时表遇到 TEXT 与 BLOB 列直接落盘,去掉这两列后单次分页从19.5 s降到4.8 s。 OFFSET 10000在5.7上改走idx_group_scope_field加 filesort,排序10,050行,9.2沿唯一键顺序读取,现状从36.1 ms变为183 ms。- D 的整组读取与全平台扫描对每个指标项做一次
uk_group_name的eq_ref,5.7上相对现状的增量分别是扩列的3.4倍与3.8倍。
原生 arm64 的 5.7 社区构建复测 A、C、D 的读取,执行计划与 amd64 一致。模拟把管理页这类短查询放大约 2 倍,但上面两处 5.7 特有的差异仍在:
| 用例 | A | C | D | D 单独读取定义 |
|---|---|---|---|---|
| 最大数据源管理页首页 | 16.8 ms | 17.9 ms | 17.2 ms | 17.4 ms |
最大数据源 OFFSET 10000 | 87.0 ms | 89.3 ms | 102 ms | 86.0 ms |
| 最大数据源管理页搜索 | 11.3 ms | 72.2 ms,C2 11.4 ms | 136 ms | 11.0 ms |
| 最大数据源族视图节点分页 | 17.1 ms | 67.2 ms,C2 56.2 ms | 132 ms | 86.5 ms |
| 最大数据源族视图首页完整步骤 | 15.3 ms | C2 56.6 ms | 无 | 91.0 ms |
| 服务视图成员查询,最大数据源 | Redis 白名单 | 13.6 ms | 15.9 ms | 无 |
服务视图成员查询,126 个服务的数据源 | Redis 白名单 | 2.10 ms | 2.45 ms | 无 |
| 服务视图完整路径,最大数据源 | 107 ms | C2 142 ms | 无 | 188 ms |
单范围读取,最大的 10 个范围 | 不涉及 | 12.5 ms | 12.1 ms | 25.1 ms |
| 最大数据源整组读取 | 101 ms | 131 ms | 195 ms | 178 ms |
| 全平台扫描 | 5.0 s | 6.5 s | 9.0 s | 8.6 s |
- [1] 镜像为
biarms/mysql:5.7(5.7.30-0ubuntu0.18.04.1,arm64),参数与数据同 a 节,复现命令为make plans57-arm64,输出在results/plans57_arm64.txt。 - [2] 社区构建比官方镜像低
14个补丁版本,只用于区分模拟与版本的影响,不替代5.7.44的结论。 - [3] D 单独读取定义没有测服务视图成员查询,搜索这一列按 PLAN 只在指标项上搜索,单范围读取这一列为先读成员再按 ID 读定义。
- [4] D 的整组读取与全平台扫描仍比 C 慢
49%、38%,OFFSET 10000仍是9.2的2.4倍,两者都来自5.7本身。 - [5] 搜索与单范围读取的方案排序与 amd64 一致,节点分页的 D 内存组装在原生构建上慢于 C 联表,同为内存组装时仍是 C2 快于 D,补测输出在
results/plans57_supplement_arm64.txt。 - [6] 首页完整步骤与服务视图完整路径只测 PLAN 写法,C 这一列为 C2,各项排序与 amd64 一致,输出在
results/plans57_supplement2_arm64.txt。
c. 关系表全量复测
C、D 的关系表装满上界规模 4,009 万行后复测,读写耗时与 a 节的 232 万行相差在 11% 以内,方案排序不变 [1]:
| 用例 [2] | A 现状 | C 元信息加列 | D 元信息建表 |
|---|---|---|---|
| 上界规模新增体积 | 0 MiB | 外推 7,697,实测 7,713 MiB | 外推 7,833,实测 7,849 MiB |
| 最大数据源管理页首页 | 35.0 → 36.8 ms | 42.3 → 43.8 ms | D2 37.3 → 38.0 ms |
| 最大数据源管理页搜索 | 20.0 → 20.2 ms | C2 20.4 → 20.8 ms | D2 20.4 → 21.0 ms |
| 最大数据源族视图首页完整步骤 | 36.4 → 37.4 ms | C2 60.6 → 63.4 ms | D2 96.2 → 100.7 ms |
| 最大数据源整组读取 | 112 → 108 ms | 148 → 144 ms | D2 221 → 217 ms |
| 全平台扫描 | 4.9 → 4.9 s | 6.8 → 6.7 s | D2 9.9 → 9.6 s |
| 服务视图成员查询,最大数据源 | Redis 白名单 | 20.9 → 20.2 ms | 27.7 → 27.4 ms |
| 服务视图完整路径,最大数据源 | 114 → 103 ms | C2 156 → 140 ms | D2 231 → 214 ms |
单范围读取,最大的 10 个范围 | 不涉及 | 33.2 → 36.2 ms | 33.4 → 34.2 ms,D2 42.2 → 43.1 ms |
bmw 每批 1,000 条上报 p50 | 不涉及 | 34.4 → 37.2 ms | 29.1 → 31.7 ms |
| 每日刷新,最大数据源 | 不涉及 | 34.9 → 37.7 s | 29.5 → 32.1 s |
- [1] 只放大关系表:成员、范围、标签与例外取值装满
40,091,444、370,456、740,912与275,164行,B 不装载,用例沿用 a 节的数据源与服务。 - [2] 全量一轮约半小时:复现命令为
make plans57-full,装载967 s,数据目录峰值11.72 GiB,输出在results/plans57_full.txt。
d. 上线风险
1,000,000 行指标项副本上的 DDL,并发管理页读与指标同步写:
| 操作 | 方案 | 耗时 | 重建 | 读最长停顿 | 读 p99 | 写最长停顿 | 写 p99 |
|---|---|---|---|---|---|---|---|
加 5 列,一条 ALTER | B、C | 68.4 s | 是 | 494 ms | 274 ms | 618 ms | 90.8 ms |
加 5 列,Django 每个字段一条 ALTER | B、C | 128.5 s | 是,5 次 | 1,186 ms | 24.3 ms | 1,485 ms | 120 ms |
删 5 列,一条 ALTER | B、C 回退 | 24.5 s | 是 | 123 ms | 9.2 ms | 390 ms | 122 ms |
删 5 列,Django 每个字段一条 ALTER | B、C 回退 | 176.1 s | 是,5 次 | 904 ms | 16.1 ms | 1,131 ms | 150 ms |
| 建五张新表 | D | 0.12 s | 否 | 1.5 ms | 1.5 ms | 3.5 ms | 3.5 ms |
| 删五张空表 | D 回退 | 0.04 s | 否 | 1.8 ms | 1.8 ms | 3.6 ms | 3.6 ms |
| 删五张有数据的新表 | D 回退 | 0.12 s | 否 | 14.3 ms | 14.3 ms | 4.9 ms | 4.9 ms |
展开表删 5 列 | B 回退 | 92.2 s | 是 | 319 ms | 14.4 ms | 373 ms | 38.7 ms |
- [1] 基线读写 p50 多在
2.2 ms~3.7 ms,最长停顿取 DDL 期间单条读写的最大耗时,D 的几项 DDL 不到0.2 s,期间只采到5个以内的样本。 - [2] 有数据的新表含
2,351,255个成员与951,119个定义,展开表为2,332,094行,按行数外推到活跃规模397 s、上界规模1,588 s。
元数据锁排队:
| 场景 | 方案 | DDL 结果 | 读最长停顿 | 写最长停顿 |
|---|---|---|---|---|
3 s 长事务期间加 5 列,lock_wait_timeout 默认 | B、C | 等锁后重建,共 34.2 s | 2,735 ms | 2,726 ms |
同上,DDL 会话 lock_wait_timeout=1 | B、C | 1205 退出 | 1,005 ms | 980 ms |
3 s 长事务期间建五张新表 | D | 0.10 s 完成 | 4.2 ms | 4.1 ms |
- [1] 长事务读一行后保持
3 s不提交,持有指标项表的共享元数据锁。 - [2] DDL 等待排他锁期间,后续读写排在它之后,
lock_wait_timeout=1把排队上限压到1 s,但 DDL 本身失败。
迁移失败,即新代码跑在只有指标项表与页面分组表的旧表结构上:
| 方案 | 报错的存量功能 | 报错的新功能 | 不受影响 |
|---|---|---|---|
| B | 管理页、指标树与指标缓存、图表、指标同步新建,均为 1054 | 服务视图 1054 | bmw 指标同步读取 |
| C | 管理页、指标树与指标缓存、图表、指标同步新建,均为 1054 | 服务视图、bmw Apply,均为 1146 | bmw 指标同步读取 |
| D | 无 | 管理页定义、服务视图、bmw Apply,均为 1146 | 全部存量功能 |
- [1] B、C 的模型含新列后,Django 查询与新建都带上新列,bmw 用 gorm
SELECT *读取不受影响,结构体含新列时新建同样报1054。 - [2] D 的管理页必须把定义作为单独语句读取,与指标项同一条语句
LEFT JOIN时新表缺失会让整个管理页报1146。
e. 正确性抽检
小数据上的 24 条断言全部符合预期:
| 检查项 | A | B | C | D |
|---|---|---|---|---|
| 指标移入自建分组后服务视图仍可见 | 否 | 是 | 是 | 是 |
search 用旧版 SDK 报 ms,其余服务为 s | 否 | 是 | 是 | 是 |
| 元信息先于指标同步到达可以落库 | 否 | 否 | 否 | 是 |
| Histogram 按原名组族 | 否 | 是 | 是 | 是 |
| 旧代码管理页仍列出全部指标 | 是 | 否 | 是 | 是 |
- [1] A 的白名单按
scope_name建键、按页面分组名查找,在脚本中模拟,ResultTableField唯一键第二行报1062。 - [2] B 移动一个指标要改写全部服务的行,样例中为
3行,C、D 只改1行。 - [3] B 的展开行
field_scope为「服务-scope_name」,旧代码按field_scope = 'default'读取查不到。
PLAN「性能基线」的原上限按 9.2 制定,D 按 PLAN 写法在 5.7 上有两项超限,PLAN 已按约 1.5 倍余量重定:
- 最大数据源管理页首页增量:D2
2.3 ms超过原上限2 ms,原生构建0.6 ms,新上限3.5 ms。 - 全平台扫描:D2
9.9 s超过原上限6 s,原生构建8.6 s,新上限15 s。
服务视图成员查询按 PLAN 路径(范围标签、上报范围、成员与例外取值)测量,最大数据源 3.8 ms、126 个服务的数据源 0.92 ms,原生构建 3.0 ms、0.68 ms,都在原上限 20 ms、2 ms 内。总表中的 27.7 ms、3.48 ms 是单条联表的成员查询,不是 PLAN 写法。
管理页搜索增量 2 ms 与族视图节点分页 150 ms 不变:
- 管理页搜索按名称:同一轮中 D2 比现状多
0.4 ms,原生构建同为0.4 ms,同一规则下 C2 也与现状持平。 - 管理页搜索按描述:若新增该搜索键,须先在定义表查出指标名,该写法为
108 ms,原生构建37.8 ms,联表写法372 ms。 - 族视图节点分页:首页完整步骤 D2 为
96.2 ms,原生构建91.0 ms,余量36%,按节点GROUP BY联表的写法仅节点分页就达208 ms。
服务视图完整路径不在基线内,D2 为 231 ms,原生构建 188 ms,比现状整组读取多 117 ms、81 ms,增量主要来自读整组定义,该服务的成员查询只占 3.8 ms。
0x03 代码证据
核对时的源码版本:bk-monitor f96e2144ba,bkmonitor-datalink b7f6acae54,链接指向 master。
| 证据 | 已核实的事实 |
|---|---|
| <源码> metadata/models/custom_report/time_series.py | [a] 指标项唯一键为 (group_id, field_scope, field_name),field_scope 默认 default[b] TimeSeriesGroup.metric_group_dimensions 非空时请求 BkBase v2,按分组键推导 field_scope |
| <源码> apm/models/datasource.py | APM 默认分组维度只有 scope_name,由白名单按业务或应用开启 |
| <源码> packages/monitor_web/scene_view/resources/custom_metric.py | 服务视图以页面分组名查找 Redis 白名单 |
| <源码> packages/apm_web/tasks.py | 白名单每 10 min 刷新,key 有效期 24 h,单条 30 d 未更新删除 |
| <源码> packages/monitor_api/apps.py | 后台库迁移除 MigrateError 外的异常被吞 |
| <源码> bk-monitor-worker/internal/metadata/service/timeseriesmetric.go | 指标同步按 (field_name, field_scope) 差集新建与失活,逐行写入 |
| <源码> bk-monitor-worker/service/scheduler/periodic/periodic.go | 指标同步每 5 min 运行,超时 10 min,无数据源级锁 |
| <源码> bkmonitor/pyproject.toml | Django 4.2.27,依赖注释要求兼容 MySQL 5.7 |
0x04 上云量级
只读查询上云环境(bkte)业务 -4228598,窗口为 2026-10-11 11:57 至 12:12:
| 应用 | 指标 | 活跃服务 | 指标 × 服务 | 倍数 |
|---|---|---|---|---|
| hpjy-microservices-activities-production | 217 | 28 | ~4,358 | 20.1 |
| hpjy-microservices-activities-press | 145 | 6 | 705 | 4.9 |
| hpyd-creation-production | 83 | 4 | 218 | 2.6 |
| sysnotice-prod-metric | 111 | 2 | 111 | 1.0 |
- [1] 最大应用整表查询超过 VM 的
500,000序列上限,按指标名前缀拆分后合计。 - [2] 最大应用在 APM 拓扑登记
126个服务,15 min内活跃28个,同名指标不跨scope_name。 - [3] 原始查询记录在 cloud_scale.txt,全部经 MCP 只读接口完成。
0x05 实验方法
| 规模 | 指标项 | 数据源 | 上报成员 | 上报范围 | 范围标签 |
|---|---|---|---|---|---|
| 活跃规模 | 1,000,000 | 7,663 | 9,952,703 | 72,379 | 144,758 |
| 上界规模 | 1,000,000 | 6,722 | 40,091,444 | 370,456 | 740,912 |
两档都含 5 个最大数据源,每个 20,000 个指标项、667 个服务、约 1,000,000 个成员。其余数据源按 bkte 四个应用的画像分为大型、中型、小型、微型,上界规模把大型数据源的服务数放大到登记数 126,覆盖 30 d 过期窗口内的上界。分档明细见 validation。
构造比例:30% 的指标为 Histogram 拆分,25% 的 scope_name 为 custom,5% 的旧指标没有元信息。成员中 1% 带例外、0.1% 带按范围覆盖、2% 已过期。
| 环境 | 取值 |
|---|---|
| 硬件 | Apple M4 Pro,14 个逻辑核,48 GiB 内存 |
| 规模压测 | MySQL 9.2.0,缓冲池 8 GiB,innodb_flush_log_at_trx_commit=1,关闭 binlog |
| 完整方案对比 | Docker MySQL 5.7.44(amd64 模拟),缓冲池 8 GiB,DYNAMIC 行格式,TCP 连接,明细见 0x02 |
| 版本兼容 | Docker MySQL 5.7.44(amd64 模拟)、8.0.46(arm64),缓冲池 2 GiB,DYNAMIC 行格式 |
| 客户端 | Python 3.11.11,pymysql 1.1.1,Unix socket,不做类型转换 |
- [1] 读取预热
30次后计300次,整组与全量扫描按用例减少次数。 - [2] 体积为
ANALYZE TABLE后information_schema的数据加索引。 - [3] Docker 卷 I/O 较慢,DDL 绝对耗时偏大,版本兼容重点看是否重建、元数据锁与执行计划是否一致。
0x06 上报关系存储
a. 展开与分表
展开方案把服务并入 field_scope,每个「指标 × 服务 × scope_name」一行指标项,活跃规模满量 9,954,746 行。分表方案按 group_id % 16 复制展开表。
| 方案 | 活跃规模行数 | 活跃规模体积 | 每行 | 上界规模体积 |
|---|---|---|---|---|
| 展开 | 9,954,746 | 7,747 MiB | 816 B | 30.5 GiB |
| 展开加分表 | 9,954,746 | 7,803 MiB | 822 B | 30.7 GiB |
| 窄成员表加定义表 | 12,120,959 | 2,856 MiB | — | 8,129 MiB |
- [1] 展开方案的上界规模按每行字节乘
40,091,444行外推,窄成员表一行为两档实测,含1,000,000行指标项。
| 用例(最大数据源) | 展开 | 展开加分表 | 窄成员表 |
|---|---|---|---|
| 管理页首页 | 1,133 ms | 1,387 ms | 15.6 ms |
| 管理页计数 | 876 ms | 1,066 ms | 14.6 ms |
管理页 OFFSET 10000 | 1,146 ms | 1,393 ms | 56.7 ms |
| 服务视图 | 105 ms | 106 ms | 13.5 ms |
| 整组读取 | 4,834 ms | 4,895 ms | 107 ms |
| 每日刷新 | 83.7 s | 82.9 s | 约 22 s |
| 全平台用例 | 展开 | 展开加分表 | 窄成员表 |
|---|---|---|---|
| 全量扫描 | 48.2 s | 48.5 s | 5.5 s |
长尾 500 个数据源整组读取 | 1,633 ms | 1,653 ms | — |
| 加列 INPLACE,最大单表 | 50.5 s | 8.6 s | 不涉及 |
| 加列 INPLACE,全部表合计 | 50.5 s | 53.2 s | 不涉及 |
- [1] 展开方案的管理页要对
1,000,000行按指标名去重,走临时表加 filesort。 - [2] 展开方案的每日刷新为 bmw 读取后逐行 UPDATE
980,000行,取3个最大数据源的平均,窄成员表按Apply每千条21.9 ms换算1,000,000个成员。 - [3] 给展开表加
(group_id, field_name)索引后首页降到3.7 ms,但按更新时间排序、深页与计数仍为1,370 ms、681 ms、1,479 ms。
分表读取没有收益:热点数据源整组落在同一张分表(最大分表 1,340,058 行),最大数据源所在分表的优化器改走唯一键,首页反而比单表慢 22%。分表只把最大单表的加列耗时从 50.5 s 降到 8.6 s,16 张表合计 53.2 s,与单表相当。
b. 窄成员表
| 表 | 活跃规模行数 | 活跃规模体积 | 上界规模行数 | 上界规模体积 | 上界规模每行 |
|---|---|---|---|---|---|
report_scope | 72,379 | 20 MiB | 370,456 | 105 MiB | 298 B |
report_scope_label | 144,758 | 25 MiB | 740,912 | 127 MiB | 180 B |
report_member | 9,952,703 | 1,663 MiB | 40,091,444 | 6,749 MiB | 177 B |
| 成员表形态(上界规模) | 数据加索引 | 主键 | 成员唯一键 | (group_id, meta_id) |
|---|---|---|---|---|
| Django 自增主键加唯一键 | 6,749 MiB | 2,326 MiB | 2,707 MiB | 1,716 MiB |
| 复合主键 | 5,889 MiB | 4,173 MiB | — | 1,716 MiB |
- [1] Django
4.2不支持复合主键,改用BIGINT自增主键后体积增加14.6%,早期INT主键加DATETIME的版本为34%。
0x07 元信息落点
a. 体积
| 方案 | 结构 | 活跃规模增量 | 上界规模增量 | 每指标增量 |
|---|---|---|---|---|
| 扩列 | TimeSeriesMetric 加 5 列 | 333 MiB | 332 MiB | 348 B |
| 一对一表 | 按 field_id 关联 | 349 MiB | 349 MiB | 366 B |
| 定义表 | 按 (group_id, field_name) 关联 | 471 MiB | 470 MiB | 492 B |
- [1] 定义行数
951,119,即95%的指标有元信息,扩列回填后指标项表从1,028 MiB膨胀到1,236 MiB。
b. 读取
上界规模的 p50,单位 ms:
| 数据源 | 用例 | 现状 | 扩列 | 一对一表 | 定义表 |
|---|---|---|---|---|---|
| 最大 | 管理页首页 | 15.4 | 16.8 | 15.6 | 15.6 |
| 最大 | 管理页 OFFSET 10000 | 36.1 | 39.6 | 46.8 | 56.7 |
| 最大 | 管理页搜索 | 23.4 | 48.6 | 73.1 | 104.5 |
| 最大 | 管理页按更新时间排序 | 47.1 | 51.4 | 47.3 | 47.1 |
| 最大 | 节点分页 | — | 49.1 | 76.8 | 120.3 |
| 最大 | 服务视图 | — | 11.0 | 11.8 | 13.5 |
| 最大 | 最热服务(14,238 个指标) | — | 112.3 | 123.0 | 140.2 |
| 最大 | 整组读取 | 82.9 | 104.3 | 104.6 | 107.2 |
| 最大 | 单指标的范围 | — | 0.08 | 0.10 | 0.10 |
| 大型 | 管理页搜索 | 0.48 | 1.38 | 1.79 | 2.34 |
| 大型 | 节点分页 | — | 1.80 | 2.14 | 2.56 |
| 大型 | 服务视图 | — | 1.16 | 1.27 | 1.43 |
- [1] 两档规模的最大数据源读取基本相同,成员增长不影响按数据源的读取。
- [2] 扩列加虚拟列
node_name与索引后,最大数据源节点分页为7.1 ms(INSTANT 加列,建索引2.3 s,63 MiB)。 - [3] 最大数据源现状已超过
20 ms:计数14.6 ms、默认排序整组 filesort47 ms、深页36 ms,与选型无关。
全量扫描(1,000,000 行加定义) | 总耗时 | 每批 p50 |
|---|---|---|
现状,500 个数据源一批 | 4.23 s | 289 ms |
现状,主键分段 10,000 行 | 4.38 s | 43 ms |
扩列,主键分段 10,000 行 | 5.51 s | 55 ms |
定义表,主键分段 10,000 行 | 5.47 s | 54 ms |
c. 写入与回收
bmw Apply 每批 1,000 条上报(11 个新成员、5 个变更定义、约 10% 到期刷新),一批一个事务:
| 用例 | 扩列 | 一对一表 | 定义表 |
|---|---|---|---|
| 最大数据源 p50 | 24.2 ms | 23.4 ms | 21.9 ms |
| 最大数据源吞吐 | 41,378 条每秒 | 42,659 条每秒 | 45,667 条每秒 |
| 混合数据源 p50 | 22.4 ms | 21.6 ms | 19.9 ms |
| 每批语句数 | 10.1 | 6.0 | 6.0 |
成员回收(5 个最大数据源,last_seen 超过 30 d,每批 1,000 行,自动提交):
| 方式 | 活跃规模单组 | 上界规模单组 | 上界规模每批 p95 | 额外索引 |
|---|---|---|---|---|
无索引 DELETE ... LIMIT | 8.46 s | 8.73 s | 933 ms | — |
| 唯一键分段扫描加主键删除 | 2.73 s | 2.75 s | 8.2 ms | — |
(group_id, last_seen) 索引 | 0.18 s | 0.19 s | 15.2 ms | 1,123 MiB |
- [1] 无索引
DELETE ... LIMIT每批都从数据源开头重扫,分段扫描每次读10,000行约26 ms。 - [2] 最大数据源单组的例外成员统计一次
834 ms,同样需要分段。
d. DDL(本机 9.2.0,1,000,000 行)
| 操作 | 耗时 |
|---|---|
扩列加 5 列 INSTANT | 0.03 s |
扩列加 5 列 INPLACE(重建) | 5.48 s |
扩列回填 100 批 × 10,000 行 | 9.69 s |
| 一对一表回填 | 4.37 s |
| 定义表回填 | 6.50 s |
e. 按范围不同的取值
同一指标的各个上报范围取值不同时,比较四种存法:
| 存法 | 表 | 成员保存 |
|---|---|---|
| 每范围一份 | 成员表 | 完整取值与指纹 |
| 内容寻址取值表 | 取值表、成员表 | 取值 ID |
| 定义加内联例外 | 定义表、成员表 | 例外取值,NULL 表示与定义相同 |
| 定义加例外引用 | 定义表、取值表、成员表 | 例外取值 ID,0 表示与定义相同 |
数据集共 1,500,000 个成员:大数据源 20,000 个指标 × 50 个范围,每个范围上报全部指标,另有 500 个数据源 × 200 个指标 × 5 个范围作为背景。例外比例分 1%、10%、50% 三档,例外成员上报旧版 SDK 的取值,描述、时间性与周期不同,单位相同。环境同 0x05,复现命令为 make meta-layout。
- [1] 每个指标的例外只有一种取值,是两种去重存法最有利的情形,例外取值彼此不同时去重收益消失。
- [2] 计时包含 pymysql 逐行读取结果集,不做类型转换,返回
20,000行的用例约一半时间花在客户端。
体积按每成员字节数外推到上界规模,单位 GiB:
| 存法 | 稳态 1% | 稳态 10% | 稳态 50% | 全体改值后 1% | 全体改值后 10% | 全体改值后 50% |
|---|---|---|---|---|---|---|
| 每范围一份 | 13.45 | 13.55 | 13.71 | 13.45 | 13.55 | 13.71 |
| 内容寻址取值表 | 5.71 | 5.85 | 5.98 | 5.81 | 5.97 | 6.11 |
| 定义加内联例外 | 5.54 | 6.24 | 9.27 | 11.17 | 13.16 | 13.95 |
| 定义加例外引用 | 5.78 | 5.92 | 6.08 | 5.88 | 6.05 | 6.20 |
- [1] 外推口径为成员表每成员字节数乘
40,091,444,加上定义表与取值表每指标字节数乘951,119个定义。 - [2] 本机
1%档实测:每范围一份515 MiB,内容寻址252 MiB,内联例外248 MiB,例外引用261 MiB。 - [3] 内联例外改值时成员行变宽引发页分裂,清空例外后 InnoDB 保留已分配的页,成员表停在改值时的高水位。
- [4] 例外引用的成员行定长,成员表在各档与改值前后都是
205 MiB,改值后的增量全部是未回收的无引用取值。
大数据源读取 p50,单位 ms:
| 存法 | 单点 | 单范围 | 单范围分两次读 | 指标默认值 1% | 指标默认值 10% | 指标默认值 50% |
|---|---|---|---|---|---|---|
| 每范围一份 | 0.046 | 38.3 | — | 940 | 1,364 | 1,911 |
| 内容寻址取值表 | 0.059 | 60.9 | 105.7 | 709 | 719 | 708 |
| 定义加内联例外 | 0.063 | 38.7 | 100.8 | 63.3 | 63.0 | 62.9 |
| 定义加例外引用 | 0.077 | 64.4 | 108.8 | 65.9 | 62.2 | 62.6 |
- [1] 单点与单范围取
1%档,单点三档都低于0.1 ms,单范围三档差异在11%以内。 - [2] 单范围取一个范围全部
20,000个指标的有效取值,用一条关联语句,分两次读为先读成员、再按 ID 列表读定义与取值。 - [3] 带取值表的存法慢在服务端关联取值表,只装载内容寻址与内联例外两种存法复测时,单范围为
48.1 ms与39.2 ms。 - [4] 没有定义行的存法用窗口函数对成员按取值计票求默认值,只在 MySQL
8.0可用。 - [5] 例外比例
50%时计票失去多数,每范围一份与内容寻址分别只有50.1%与55.7%的指标计票结果等于定义。 - [6] 改为取任意一个成员可降到
134 ms~161 ms,但1%档有1.02%的指标取到例外值,10%档为10.08%。
写入取 1% 档:
| 存法 | 每批写入 p50 | 每批语句 | 全体改值写入行数 | 全体改值耗时 |
|---|---|---|---|---|
| 每范围一份 | 6.59 ms | 2 | 1,000,000 | 14.1 s |
| 内容寻址取值表 | 8.65 ms | 5 | 1,031,217 | 12.4 s |
| 定义加内联例外 | 13.50 ms | 3 | 2,006,731 | 31.0 s |
| 定义加例外引用 | 13.65 ms | 6 | 2,037,948 | 22.9 s |
- [1] 每批
1,000条上报,其中10条改为新取值,一批一个事务,三档差异在1 ms以内。 - [2] 全体改值把大数据源
1,000,000个成员的单位从ms改为s,沿成员唯一键每段10,000个成员、一段一个事务。 - [3] 有定义行的存法随后整理,晋升
20,000个定义并清空986,731个同值例外,例外引用比较整数,清空快约40%。 - [4] 内容寻址与例外引用改值后各留下
31,235个无引用取值,需要额外回收。
定义行把「指标默认值」从按成员计票变为按指标读取,这是两种带定义的存法胜出的原因。两者的差别在例外放在哪里:内联例外读取更快、每批语句更少,但体积随例外比例上升,全体改值后停在接近每范围一份的高水位。例外引用的成员行定长,体积在各档与改值前后都稳定在 5.8 GiB~6.2 GiB,代价是单范围读取多一次关联与取值回收,选型取例外引用。
0x08 MySQL 5.7 与 8.0
a. 在线 DDL
1,000,000 行指标项表(676 MiB),并发读写探测元数据锁等待:
| 操作 | 5.7.44 | 8.0.46 |
|---|---|---|
扩列加 5 列 INSTANT | 不支持(1800) | 0.34 s,不重建 |
扩列加 5 列 INPLACE | 37.5 s,重建 | 38.1 s,重建 |
Django 单个可空 AddField | 24.1 s,重建 | 0.03 s,自动 INSTANT |
| 加索引 INPLACE | 1.4 s | 5.2 s |
回滚扩列:删 5 列 INSTANT | 不支持 | 0.02 s |
长事务 3 s 期间的 ALTER | 后续读写排队 2.7 s | 后续读写排队 2.7 s |
同上,ALTER 会话 lock_wait_timeout=1 | 1205 退出,读写只受阻 1.0 s | 同左 |
- [1] Django
8.0.13+的 JSON 非空列形态DEFAULT ('{}')在5.7报1064,在8.0不支持 INSTANT(1845),新列只能可空。 - [2] 0x02 在完整画像的指标项副本上复测,
5.7一条 ALTER 加5列重建68.4 s,Django 逐列128.5 s。
b. 建表与特性
| 特性 | 5.7.44 | 8.0.46 | 结论 |
|---|---|---|---|
| 定义、范围、标签与成员四张表(DYNAMIC) | 成功 | 成功 | 可用,取值表未单独建表验证,所用列类型均已覆盖 |
utf8mb4 唯一键改 COMPACT | 1071 | 1071 | 索引文本列不用 utf8mb4 |
utf8 varchar(255) 唯一键改 COMPACT | 成功 | 成功 | 单列 765 B 不超过 767 B |
innodb_large_prefix=OFF | 1071 | 已移除 | 5.6 升级上来的实例需检查 |
窗口函数、CTE、JSON_TABLE、多值索引 | 不支持 | 支持 | 不使用 |
| 函数索引 | 不支持 | 支持 | 用生成列加索引替代 |
CHECK 约束 | 接受但不生效 | 生效 | 不依赖 |
INSERT ... AS new | 不支持 | 支持 | 用 VALUES() |
SKIP LOCKED、NOWAIT | 不支持 | 支持 | 不使用 |
RENAME COLUMN | 不支持 | 支持 | 不使用 |
utf8mb4_0900_ai_ci | 不支持 | 支持 | 不使用 |
c. upsert
每批 1,000 行:
| 写法 | 全部已存在 | 半数新增 | 自增消耗 |
|---|---|---|---|
ON DUPLICATE KEY UPDATE col = VALUES(col) | 84.9 ms(5.7) | 52.7 ms(5.7) | 1,000 |
行别名 AS new | 5.7 不支持 | 5.7 不支持 | 1,000 |
INSERT ... SELECT 派生表 | 87.4 ms(5.7) | 54.0 ms(5.7) | 新增行数 |
- [1]
autoinc_lock_mode=1下VALUES()形式无论行是否存在都消耗整批自增 ID,成员与标签表必须用BIGINT,并优先先查后插。
0x09 执行计划
2,000 个数据源、199,910 个指标项、1,999,100 个成员,最大数据源 20,000 个指标 × 10 个范围。两版本执行计划一致,以下为 5.7 中位数,管理页用例取最大数据源:
| 用例 | 耗时 | 读取行数 | 结论 |
|---|---|---|---|
管理页首页,未带 field_scope | 7.18 ms | 20,101 | 走 (group_id, scope_id, field_name) 加 filesort |
管理页首页,带 field_scope='default' | 0.59 ms | 50 | 走唯一键顺序,无 filesort |
| 服务倒排经定义表回指标项 | 6.86 ms | 8,005 | 带 field_scope 走唯一键 |
同上,漏写 field_scope | 7,996 ms | 40,008,005 | 回查指标项必须带 field_scope='default' |
| 两个标签求交 | 2.71 ms | 2,007 | 标签唯一键覆盖 |
scope_name(utf8)关联 label_value(utf8mb4) | 0.37 ms | 101 | 能用索引但驱动顺序反转,新表统一 utf8 避免混用 |
成员批量存在性,行构造 IN | 10.1 ms | 1,000 | 优于 OR 展开的 30.2 ms |
无 last_seen 索引删除(RR) | 672 ms | 锁 250,440 行 | 改为分段扫描加主键删除 |
0x10 早期方案与 __scope 表示
a. 早期方案矩阵
早期以 695,000 个指标项比较了五种上报关系存储,结论已被两档满量压测替代,保留用于说明淘汰原因:
| 结构 | 淘汰原因 |
|---|---|
服务并入 field_scope | 单个大应用日常刷新 1,433 ms、19,644 条语句,即本报告的展开方案 |
成员行引用 field_id,例外单独成行 | 读取例外多一次关联,体积与采用方案相当 |
| 指标项保存来源 ID 的 JSON 列表 | 依赖 MySQL 8.0.17 多值索引 |
| 来源保存指标 ID 的 JSON 列表 | 依赖多值索引,按指标反查最慢 |
| 定义、范围与成员各一张表 | 采用,即本报告的窄成员表加定义表 |
- [1] 明细见本地
results/s1_matrix.txt、s2_scale.txt、s2_layout.txt。
b. __scope 表示
| 表示 | 体积(345,000 个范围) | 精确定位 p50 | 按服务过滤 p50(126 个服务) | 按服务过滤 p50(1,000 个服务) |
|---|---|---|---|---|
| 规范串唯一键 | 124 MiB | 0.038 ms | 0.151 ms | 0.899 ms |
| 哈希唯一键 | 84 MiB | 0.042 ms | 不支持 | 不支持 |
| 哈希唯一键与标签倒排 | 216 MiB | 0.042 ms | 0.050 ms | 0.070 ms |
| 哈希唯一键与 JSON 多值索引 | 228 MiB | 0.042 ms | 0.056 ms | 0.081 ms |
| 哈希唯一键与固定生成列 | 164 MiB | 0.042 ms | 0.048 ms | 0.079 ms |
- [1] 规范串唯一键按服务过滤只能对规范串做
LIKE匹配,服务数从126增至1,000时 p50 增长6倍,标签倒排只增长40%。 - [2] JSON 多值索引依赖 MySQL
8.0.17,固定生成列每种范围类型都要加列。