联合索引的规则说起来就一句“最左前缀”,但为什么是这样、什么情况下会失效、覆盖索引又怎么省掉回表,值得用具体的例子走一遍。
B+ 树里到底按什么排序
CREATE INDEX idx_abc ON t (a, b, c);
这棵索引树按 a 排序;a 相同的时候按 b 排序;a、b 都相同才按 c 排序。就像电话簿按“姓、名”排序——你可以快速找到“张”姓的所有人,也可以找“张伟”,但没法直接找“名叫伟”的所有人。
所以:
WHERE a=1 AND b=2 AND c=3 -- 全用上
WHERE a=1 AND c=3 -- 只用上 a
WHERE b=2 AND c=3 -- 完全用不上
WHERE a=1 AND b>2 AND c=3 -- 只用上 a、b(c 失去有序性)
范围查询后面的列为什么用不上
b > 2 命中多行,这些行里 c 的排列并不全局有序(只在每个 b 值内部有序),MySQL 不能再用 c 做定位,只能在过滤出的行里逐条比较。这就是“范围之后失效”。
如果业务确实要 a=1 AND b>2 AND c=3,可以把索引改成 (a, c, b),让等值条件 c 排在范围条件 b 前面。
覆盖索引:不回表
普通二级索引的叶子节点存的是主键值。查到索引项之后还要拿主键回主键索引取整行,这叫回表。如果查询需要的列全都在索引里,就不用回表:
-- 只查 a、b,索引里都有
SELECT a, b FROM t WHERE a = 1;
-- Extra: Using index
Using index 就是覆盖索引生效的标志。列表页这种“只取少量字段、数据量大”的场景,覆盖索引收益非常明显——可能从几千次随机 IO 变成一次顺序扫描。
代价是索引变宽。把 title 这种长字符串塞进索引,索引文件会膨胀好几倍,写入也变慢。需要权衡。
索引列顺序怎么定
三条实用原则:
- 区分度高的列放前面(
status只有三种值,放前面过滤效果差) - 等值条件放前面,范围条件放后面
- 把
ORDER BY的列放在最后,可以顺带消掉 filesort
一个真实例子
文章列表查询:WHERE status='publish' AND lang='zh' ORDER BY created_at DESC LIMIT 20。
索引建成 (status, lang, created_at):两个等值条件定位,created_at 有序,取前 20 行就停。如果顺序建成 (created_at, status, lang),就只能沿时间倒序扫,逐行判断状态,扫几千行才凑够 20 条。
同一个查询,索引列顺序不同,性能差了两个数量级。这就是为什么建索引不能只看“哪些列出现在 WHERE 里”,要看完整的查询形态。
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:https://mi-blog.cn/archives/665
共有 0 条评论