mysql索引最左匹配原则
在 MySQL 中,最左前缀原则(Leftmost Prefix Principle)是针对复合索引(多列索引)如何被查询优化器有效使用的核心规则。理解这个原则对于设计高效的索引和编写优化查询至关重要。
核心定义:
一个复合索引 INDEX (col1, col2, col3, ...)
在磁盘上存储时,是按照索引定义中列的顺序从左到右进行排序的。最左前缀原则规定:
查询的 WHERE
子句或 JOIN
条件中,必须包含复合索引的最左边的一个或多个列,并且这些列在查询条件中出现的顺序必须与索引定义中的顺序一致(可以跳过右边的列,但不能跳过左边的列),MySQL 才能有效地利用这个复合索引。
关键点解释:
必须从最左列开始: 查询条件中必须包含索引定义中的第一列(
col1
)。如果查询条件中没有col1
,即使包含了col2
、col3
等,MySQL 通常也无法使用这个复合索引进行高效查找(可能会进行全表扫描或只使用部分后续列的索引,但效率远不如使用最左列)。顺序一致(可跳过右边列):
查询条件中的列顺序不需要严格和索引定义的列顺序完全相同(比如
WHERE col1 = ... AND col3 = ...
)。但是,被使用的列在索引定义中的顺序必须是连续的,并且从最左边开始。你可以理解为只能使用索引定义中列的一个前缀子集。
例子:索引
(A, B, C)
WHERE A = ?
(使用A
)WHERE A = ? AND B = ?
(使用A, B
)WHERE A = ? AND C = ?
(使用A
,C
无法直接利用索引定位,但A
可以缩小扫描范围,C
可能作为过滤条件在索引扫描后应用)WHERE B = ?
(缺少最左列A
,无法使用索引)WHERE B = ? AND C = ?
(缺少最左列A
,无法使用索引)WHERE A = ? AND B > ? AND C = ?
(使用A, B
进行查找和范围扫描,C
在B
的范围扫描结果内作为过滤条件。范围查询B > ?
之后的列C
无法再用于索引查找,只能过滤)范围查询的影响: 如果查询条件中对索引中的某一列使用了范围查询(如
>
,<
,BETWEEN
,LIKE 'prefix%'
),那么:该列本身及其左边的列仍然可以用于索引查找。
该列右边的所有列将无法再用于索引查找(即无法再缩小扫描范围),它们只能在扫描到符合范围条件的行之后,作为额外的过滤条件(Using where)来应用。
例子:索引
(A, B, C)
WHERE A = 10 AND B > 20 AND C = 30
A=10
用于精确查找。B>20
用于范围扫描。C=30
无法再用于索引查找定位,只能在扫描到的A=10 AND B>20
的结果集中,对每一行检查C=30
是否为真(Using where)。覆盖索引: 最左前缀原则不仅适用于
WHERE
子句,也适用于ORDER BY
和GROUP BY
子句。更重要的是,如果一个查询只需要访问索引中包含的列(即索引“覆盖”了查询所需的所有列),那么即使它只使用了索引的最左前缀部分,MySQL 也只需扫描索引本身而无需回表查询数据行,这可以极大提升性能(Extra: Using index)。
为什么有这个原则?
这与 B+Tree 索引的物理存储结构有关。复合索引 (A, B, C)
在 B+Tree 中是先按 A
排序,在 A
相同的情况下按 B
排序,在 A
和 B
都相同的情况下才按 C
排序。如果没有最左边的列 A
,数据库就不知道从索引树的哪个分支开始查找,相当于失去了有序性,只能退回到低效的全表扫描。
总结与最佳实践:
设计索引: 创建复合索引时,仔细考虑列的顺序。将最常用于等值查询、区分度高(唯一值多)的列放在左边。将用于范围查询或排序的列放在等值查询列之后,或者考虑单独为它们建索引。
编写查询: 确保你的
WHERE
/JOIN
/ORDER BY
/GROUP BY
子句能够利用到复合索引的最左前缀。如果查询条件无法包含最左列,考虑为该查询单独创建一个不同的索引。利用覆盖索引: 尽量让查询只选择索引列,避免
SELECT *
,这样即使查询只用了索引的一部分,也可能通过覆盖索引获得高性能。使用
EXPLAIN
: 在 SQL 语句前加上EXPLAIN
,查看 MySQL 的执行计划。关注possible_keys
(可能使用的索引)、key
(实际使用的索引)、key_len
(使用的索引长度,反映了使用了索引的多少前缀列)以及Extra
列(如Using index
表示覆盖索引,Using where
表示索引扫描后进行了过滤)。这是验证查询是否有效利用最左前缀原则的最直接方法。