mysql复合索引的最左匹配原则是指在使用多列组成的复合索引时,查询条件必须从索引的最左列开始匹配,才能触发索引生效。理解这个原则需要先了解复合索引的存储结构,才能明白为什么索引的生效顺序有严格要求。

复合索引的底层存储逻辑
mysql的复合索引本质上是B+树结构,和普通单列索引的区别在于,索引的排序是按照创建索引时指定的列顺序依次进行的。比如我们创建一个包含三列的复合索引:
-- 创建测试表
CREATE TABLE user_info (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
age INT,
city VARCHAR(50),
INDEX idx_name_age_city (name, age, city)
);
上面的idx_name_age_city索引的B+树排序规则是:先按照name字段排序,name相同的记录再按照age排序,age相同的记录最后按照city排序。这种排序特性直接决定了最左匹配原则的规则。
最左匹配原则的核心规则
最左匹配原则的核心要求可以总结为以下几点:
- 查询条件必须包含复合索引的最左列,否则索引完全不生效
- 查询条件可以跳过中间列,但跳过的列之后的列无法使用索引
- 范围查询(比如
>、<、LIKE 'xx%'等)会终止后续的索引匹配 - 索引列的顺序需要和查询条件的顺序无关,mysql优化器会自动调整条件顺序匹配索引
不同查询场景的索引生效分析
我们基于上面创建的user_info表和idx_name_age_city索引,分析不同查询语句的索引使用情况。
完全匹配索引所有列
当查询条件包含索引的所有列,且都是等值查询时,整个索引都会生效:
-- 索引完全生效,使用idx_name_age_city全部三列 EXPLAIN SELECT * FROM user_info WHERE name = '张三' AND age = 20 AND city = '北京';
执行计划的key字段会显示idx_name_age_city,key_len会对应三列的总长度。
只匹配索引的前缀列
查询条件只包含索引最左边的部分列时,索引只会使用匹配到的前缀部分:
-- 索引生效,只使用name列 EXPLAIN SELECT * FROM user_info WHERE name = '张三'; -- 索引生效,使用name和age两列 EXPLAIN SELECT * FROM user_info WHERE name = '张三' AND age = 20;
跳过中间列的情况
如果查询条件包含最左列和后面的列,但跳过了中间列,那么只有最左列会使用索引:
-- 只使用name列索引,age和city无法使用索引 EXPLAIN SELECT * FROM user_info WHERE name = '张三' AND city = '北京';
包含范围查询的情况
当查询条件中出现范围查询时,范围列之后的索引列无法生效:
-- name列使用索引,age是范围查询,city无法使用索引 EXPLAIN SELECT * FROM user_info WHERE name = '张三' AND age > 20 AND city = '北京'; -- name和age使用索引,city无法使用索引 EXPLAIN SELECT * FROM user_info WHERE name = '张三' AND age BETWEEN 20 AND 30 AND city = '北京';
条件顺序不影响匹配
mysql的查询优化器会自动调整条件的顺序来匹配索引,所以条件的书写顺序不需要和索引列顺序一致:
-- 条件顺序和索引列顺序不一致,依然会使用name和age两列索引 EXPLAIN SELECT * FROM user_info WHERE age = 20 AND name = '张三';
最左列缺失的情况
如果查询条件不包含复合索引的最左列,那么整个复合索引都不会生效:
-- 不包含name列,复合索引完全不生效 EXPLAIN SELECT * FROM user_info WHERE age = 20 AND city = '北京';
常见误区说明
很多开发者会误以为查询条件只要包含索引中的列就会生效,或者认为索引列的顺序需要和查询条件顺序完全一致,这些认知都是错误的。另外需要注意,对索引列使用函数、进行运算、或者进行隐式类型转换,都会导致该列无法使用索引,即使符合最左匹配原则也会失效。
比如下面的查询语句,虽然包含了name列,但因为对name使用了函数,所以索引不会生效:
-- 对name使用函数,索引失效 EXPLAIN SELECT * FROM user_info WHERE LEFT(name, 2) = '张三';
复合索引创建建议
在创建复合索引时,建议将选择性高的列放在最左边,选择性可以通过COUNT(DISTINCT 列)/COUNT(*)计算,数值越高选择性越好。同时要根据实际查询场景调整索引列的顺序,优先保证高频查询的条件能够匹配最左前缀,避免创建冗余的复合索引。