知识卡片

"特殊用户"案例:异常基数能让看似合理的索引完全失效

普通读书笔记卡

内容

选择性/基数这类经验法则默认数据分布是”正常”的,但真实系统里的数据经常 不是均匀分布,一旦出现极端偏斜的特殊值,按平均情况建立的索引可能在关键 场景下完全不管用。书中给出一个真实案例:某论坛的Message表建了 (groupId, userId)这个多列索引,从EXPLAIN的输出看这个选择顺序是合理的 ——问题在于这张表的数据是从另一个系统迁移过来的,迁移过程中把几乎全部 的行(4,142,217行里有4,092,654行,占比接近99%)都挂在了同一个管理员账号 的groupId下。这意味着一旦查询条件命中这个管理员groupId,索引的第一层 过滤几乎不起作用——这个值本身就覆盖了几乎全表,索引形同虚设,而这种 退化在只看”平均”选择性或”表面上看起来正常”的EXPLAIN输出时完全不会 暴露出来。这个案例把 [[多列索引列序选择性优先不如避免随机IO和排序重要]]和 [[前缀索引长度要按最坏情况选择性而非平均值来定]]两条卡片共同指向的道理 落到了一个具体、可复现的场景上:索引设计时依赖的选择性/基数统计,本质 上是对”典型查询会遇到什么样的值”的一个统计假设,而管理员账号、访客/ 游客账号、被批量导入或迁移的历史数据、拥有异常多好友/粉丝的用户,这些 “特殊用户”或特殊记录恰恰最容易打破这个假设——它们不是统计上的噪声, 而是会被查询频繁命中的真实业务对象。评估一个索引是否可靠,除了看平均/ 典型情况,还应该主动检查数据里是否存在这类已知会产生异常基数的特殊值, 并针对性验证索引在这些值上的表现。

参考来源

- 位置:《高性能MySQL:第3版》第5章"创建高性能的索引"5.3.4节"选择合适 的索引列顺序"(源文件:_epub-src/OEBPS/Text/part0012.xhtml) - 结论依据:原文明确"这些数据都是从其他系统迁移过来的,且几乎所有的行 都属于一个管理员账号……如果查询中带上了这个管理员用户的groupId,则 MySQL不得不检查数百万行数据",直接说明数据迁移造成的极端基数偏斜 如何使一个EXPLAIN上看起来合理的索引在关键场景下失效。 - 原始内容:这些数据都是从其他系统迁移过来的,且几乎所有的行都属于一个 管理员账号……如果查询中带上了这个管理员用户的groupId,则MySQL不得不 检查数百万行数据。