MySQL联合索引最左匹配原则

好的,我们来深入浅出地讲解 MySQL 中最重要、最核心的索引使用原则之一:​联合索引的最左匹配原则

图片[1]_MySQL联合索引最左匹配原则_知途无界

一、什么是联合索引?

联合索引​(Compound Index / Composite Index),也叫复合索引,是指一个索引包含多个列(字段)。

例如,我们有一个用户表 users,经常需要根据 last_namefirst_name 来查询用户,那么我们可以创建一个联合索引:

CREATE INDEX idx_name ON users(last_name, first_name);

这个 idx_name 就是一个包含了 last_namefirst_name 两个字段的联合索引。

二、什么是最左匹配原则?

最左匹配原则​(Leftmost Prefix Principle)是 MySQL 建立和使用联合索引的“潜规则”。

它的核心思想是:MySQL 的联合索引在查询时,会从索引定义的最左边的列开始匹配,并且不会跳过中间的列。只有当索引中的前缀列(从左开始的连续列)被用于查询条件时,索引才会被有效地使用。​

可以把联合索引想象成一本书的目录,目录是按“章 -> 节 -> 小节”的顺序编排的。如果你想查找某个小节的内容,你必须先从“章”开始找,然后是“节”,最后才能定位到“小节”。你不能跳过“章”或“节”直接去找“小节”,那样目录就失效了。


三、原理剖析:索引是如何存储的?

要理解最左匹配,首先要知道联合索引在底层 B+Tree 数据结构中的存储方式。

对于索引 INDEX (last_name, first_name),MySQL 会先按 last_name 进行排序,在 last_name 相同的情况下,再按 first_name 排序。

简化的索引树结构示意:​

last_namefirst_name…其他列…
AdamsAlice
AdamsBob
AdamsCharlie
BakerDavid
BakerEve
ClarkFrank

关键点:​

  1. 整棵树首先按 last_name 排序。所有 last_name='Adams' 的记录会聚集在一起。
  2. last_name='Adams' 这个分组内,再按 first_name 排序(Alice, Bob, Charlie)。

正是因为这种“先按第一列排序,再按第二列排序”的结构,决定了查询必须从最左列开始使用。​


四、实战演练:哪些查询能用上索引?

假设我们有索引 idx_name (last_name, first_name)

✅ 情况1:查询条件包含最左列(全值匹配)

-- 会使用索引,并且是最高效的使用方式
SELECT * FROM users 
WHERE last_name = 'Adams' AND first_name = 'Alice';

分析​:查询从最左列 last_name 开始匹配,并且连续使用了索引的两列。MySQL 可以快速定位到 last_name='Adams' 的分支,然后在该分支下精确找到 first_name='Alice' 的记录。

✅ 情况2:只查询最左列(前缀匹配)

-- 会使用索引,但只用到索引的第一列
SELECT * FROM users 
WHERE last_name = 'Adams';

分析​:查询条件正好从索引的最左列开始,并且没有跳过它。MySQL 可以使用索引快速定位所有 last_name='Adams' 的记录。虽然没用到 first_name,但索引依然有效。

✅ 情况3:查询条件包含最左列及中间列(范围匹配)

-- 会使用索引,但只用到索引的前两列(到范围查询列为止)
SELECT * FROM users 
WHERE last_name = 'Adams' AND first_name LIKE 'A%';

分析​:last_name 是等值查询,first_name 是范围查询。MySQL 会使用索引找到所有 last_name='Adams' 的记录,然后在这些记录中,利用索引继续查找 first_name 以 ‘A’ 开头的记录。​注意​:在范围查询之后的列,索引就失效了。

❌ 情况4:跳过最左列(失效!)

-- 不会使用索引!全表扫描
SELECT * FROM users 
WHERE first_name = 'Alice';

分析​:查询条件直接从第二列 first_name 开始。在索引树中,first_name 的值是无序的(只有在 last_name 相同的组内才有顺序)。因此,MySQL 无法通过索引直接定位到 first_name='Alice' 的记录,只能进行全表扫描。

❌ 情况5:最左列条件为范围查询,后面列条件无效(部分失效)

-- 会使用索引,但只用到第一列(last_name)
SELECT * FROM users 
WHERE last_name LIKE 'A%' AND first_name = 'Alice';

分析​:last_name 使用了范围查询(LIKE 'A%'),这会导致 MySQL 无法再利用索引对 first_name 进行精确查找。索引只被用于筛选 last_name 以 ‘A’ 开头的记录,对于 first_name 的条件,需要回表后在数据行中进行过滤。

❌ 情况6:顺序颠倒(不一定失效,但优化器会调整)

-- 现代MySQL优化器会自动优化WHERE条件顺序
-- 所以这个查询依然会使用索引,效果和情况1一样
SELECT * FROM users 
WHERE first_name = 'Alice' AND last_name = 'Adams';

分析​:虽然 SQL 语句中 first_name 写在了前面,但 MySQL 的查询优化器足够智能,它会识别出 last_name 是索引的最左列,从而自动调整查询顺序来使用索引。​但是,依赖优化器是不好的习惯,写 SQL 时应尽量遵循最左匹配顺序。​


五、如何利用最左匹配原则优化索引?

  1. 选择合适的索引列顺序
    • 高频查询列在前​:将 WHERE 子句中最常作为查询条件的列放在最左边。
    • 高选择性列在前​:选择性(Cardinality)高的列(即列中唯一值多的列)放在前面,能更快地缩小查询范围。例如,gender(男/女)选择性低,email 选择性高,通常 email 更适合放在前面。
    • 兼顾排序和分组​:如果查询中经常有 ORDER BYGROUP BY,可以考虑将排序列加入索引,并放在合适的位置。例如,INDEX (last_name, first_name) 可以优化 ORDER BY last_name, first_name
  2. 覆盖索引(Covering Index)​
    如果索引包含了查询所需要的所有字段,MySQL 就无需回表查询数据行,效率极高。 -- 假设索引为 INDEX (last_name, first_name, email) SELECT last_name, first_name, email FROM users WHERE last_name = 'Adams'; 这个查询只需要 last_name, first_name, email 三个字段,它们都在索引 idx_name 中,所以 MySQL 只需扫描索引树即可完成查询,速度极快。

总结

查询条件是否使用索引说明
WHERE last_name = ?使用索引第一列
WHERE last_name = ? AND first_name = ?使用索引全部列
WHERE last_name = ? AND first_name LIKE 'A%'使用索引前两列
WHERE first_name = ?跳过最左列,索引失效
WHERE last_name LIKE 'A%' AND first_name = ?⚠️只使用索引第一列
WHERE first_name = ? AND last_name = ?优化器会调整顺序,但建议按索引顺序写

牢记口诀:带头大哥不能死,中间兄弟不能断。​​ 只要你的查询条件从联合索引的第一列开始,并且中间没有跳过列,索引就会为你所用。

© 版权声明
THE END
喜欢就点个赞,支持一下吧!
点赞66 分享
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容