知识卡片

前缀索引长度要按最坏情况选择性而非平均值来定

普通读书笔记卡

内容

索引选择性是”不重复索引值个数(基数)÷表总行数”,值越接近1说明索引区分度 越高、查询过滤效果越好,唯一索引选择性恰好是1。对于很长的字符列(BLOB/ TEXT/超长VARCHAR),MySQL不允许索引全部长度,只能索引前面一段字符作 “前缀索引”,用索引体积换取效率,但代价是选择性通常会下降。选前缀多长是 个平衡:太短,选择性不够,过滤效果差;太长,索引膨胀失去意义。常规做法是 不断增加前缀长度,观察前缀的选择性何时逼近整列的选择性,找到”选择性增幅 明显变缓”的临界点。但这里有个容易被绕过去的陷阱:只看平均选择性会得出 过于乐观的结论——用一批模拟城市名数据做实验时,前缀长度为4的”平均”选择性 看起来已经不错,但实际统计”前缀为4时出现次数最多的那个值”,会发现它的 出现频率明显高于”整列里出现次数最多的城市”,也就是说存在选择性远低于平均 水平的”最坏情况”前缀值(真实场景里,”San”、”New”开头的城市名会把这种问题 放得更大)。这说明前缀长度的选择不能只按平均选择性拍板,还要专门检查最 常见前缀的分布,确认最坏情况下的选择性也过得去,否则线上遇到这些高频前缀 时,索引会退化得比平均表现差得多。另外前缀索引有个结构性限制:因为索引里 只存了列值的一部分,MySQL无法用它做ORDER BY、GROUP BY,也无法把它当 [[覆盖索引让查询只读索引无须回表]]使用——这是用前缀索引换体积时必须 接受的功能代价,不是bug。

参考来源

- 位置:《高性能MySQL:第3版》第5章"创建高性能的索引"5.3.2节"前缀索引和 索引选择性"(源文件:_epub-src/OEBPS/Text/part0012.xhtml) - 结论依据:原文明确"只看平均选择性是不够的,也有例外的情况,需要考虑 最坏情况下的选择性。平均选择性会让你认为前缀长度为4或者5的索引已经 足够了,但如果数据分布很不均匀,可能就会有陷阱……MySQL无法使用前缀 索引做ORDER BY和GROUP BY,也无法使用前缀索引做覆盖扫描",直接说明 平均选择性的误导性及前缀索引的功能限制。 - 原始内容:只看平均选择性是不够的,也有例外的情况,需要考虑最坏情况下 的选择性……如果前缀是4个字节,则最常出现的前缀的出现次数比最常出现的 城市的出现次数要大很多。即这些值的选择性比平均选择性要低。