【一文吃透】MySQL 最左匹配原则,99% 的人都被坑过!
别再说你懂 MySQL 索引了,“最左匹配原则” 真正掌握的人其实很少!
很多人写 SQL 时喜欢加各种索引,却发现查询一点也没加快?
很可能你被一个关键词坑了:最左匹配。
今天这篇文章,我们就把它讲透!无废话、全实战、通俗易懂!
一、什么是最左匹配原则?
先看一句话:
联合索引在使用时,必须从最左边的字段开始连续匹配,才能命中索引。
举个最经典的例子:
CREATE INDEX idx_user ON users (age, gender, city);
上面创建了一个联合索引,字段顺序是:
age → gender → city
那么以下查询能否使用索引?
查询语句 | 能否用索引 | 原因说明 |
---|---|---|
WHERE age = 30 | ✅ 可以 | 命中最左字段 |
WHERE age = 30 AND gender = 'M' | ✅ 可以 | 匹配了最左两列 |
WHERE gender = 'M' | ❌ 不行 | 跳过了最左字段 age |
WHERE city = 'Beijing' | ❌ 不行 | 最左的 age、gender 都跳过了 |
WHERE age = 30 AND city = 'Beijing' | ✅ 可以(部分匹配) | 虽然中间跳了 gender,但匹配了前缀 age,仍能用索引 |
WHERE gender = 'M' AND age = 30 | ✅ 可以(MySQL 自动优化) | 虽然顺序调换,但条件都有,只要字段不被函数干扰就行 |
二、为什么叫“最左”?
MySQL 创建联合索引时,其实会隐式地构造多个前缀索引,比如上面的:
INDEX (age, gender, city)
等价于自动生成了:
- (age)
- (age, gender)
- (age, gender, city)
注意:并不会生成 (gender)
或 (gender, city)
。
所以,只有你从“左边开始”,MySQL 才知道你在用哪个索引“前缀”。
三、几个常见误区 ⚠️
1. 你写的顺序和定义顺序不一致
-- 索引是 (age, gender)
-- 但你写:
SELECT * FROM users WHERE gender = 'M' AND age = 30;
虽然逻辑等价,但很多版本下可能无法使用索引。最佳实践是顺序一致。
2. 中间字段断裂了
-- 索引是 (age, gender, city)
-- 只用了 age 和 city
SELECT * FROM users WHERE age = 30 AND city = 'Shanghai';
虽然用了 age,但跳过了 gender,部分版本能优化但不稳定,建议避免跳跃中间字段。
3. 使用了函数或运算
-- 错误:用了函数,导致无法使用索引
WHERE LEFT(name, 3) = 'Tom'
在索引字段上使用函数(如 LEFT()
、DATE()
、CAST()
)会让索引失效。
4. 使用范围查询中断匹配
-- 索引为 (age, gender, city)
SELECT * FROM users WHERE age > 30 AND gender = 'M' AND city = 'Beijing';
范围查询 (>
, <
, BETWEEN
) 一旦出现,后面的字段将无法再用索引!
解释:范围查询会“切断”索引匹配。
四、图解最左匹配原理
设我们有如下联合索引:
(age, gender, city)
它就像一本多重排序的字典:
第1层:age 排序
第2层:在相同 age 中,gender 排序
第3层:在相同 age 和 gender 中,city 排序
你必须按这个顺序去查,它才能帮你快速定位。
跳着查、跳着匹配,它就懵了,只能全表扫描。
五、实战案例:到底谁能用到索引?
表结构如下:
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(100),
department VARCHAR(100),
position VARCHAR(100),
city VARCHAR(100),
INDEX idx_dept_pos_city (department, position, city)
);
我们来看下面这些 SQL 哪些能命中索引:
查询语句 | 是否命中索引 | 原因 |
---|---|---|
WHERE department = 'IT' | ✅ 是 | 最左前缀匹配 |
WHERE position = 'Manager' | ❌ 否 | 跳过了最左的 department |
WHERE department = 'IT' AND position = 'Manager' | ✅ 是 | 前缀匹配两个字段 |
WHERE department = 'IT' AND city = 'Beijing' | ✅ 是 | 匹配最左字段 + 跳过中间字段(可能部分优化) |
WHERE city = 'Beijing' | ❌ 否 | 跳过太多,索引失效 |
WHERE department LIKE 'I%' | ✅ 是 | 右边模糊可用索引 |
WHERE department LIKE '%IT' | ❌ 否 | 左边模糊索引失效 |
六、怎么写才不会踩坑?🔥实用建议🔥
✅ 索引字段顺序要考虑查询频率:把最常作为查询条件的放在最前。
✅ 避免函数/表达式参与查询条件:
-- 不推荐:
WHERE DATE(created_time) = '2024-01-01'
-- 推荐:
WHERE created_time >= '2024-01-01' AND created_time < '2024-01-02'
✅ 使用 EXPLAIN 观察是否命中索引:
EXPLAIN SELECT * FROM users WHERE age = 30;
看是否用了 idx_user
,是否为 ref
或 range
类型。
✅ 避免中断匹配的范围条件(比如 <
、>
、BETWEEN
)
✅ 善用覆盖索引(Covering Index),只查询索引中的字段,可避免回表:
-- 如果 idx_user 包含 name、email
SELECT name, email FROM users WHERE name = 'Tom';
七、一句话总结
联合索引的“最左匹配”原则是 MySQL 查询能否提速的核心关键点!
如果你忽略了这个规则,就像买了 VIP 却走了普通通道,白白浪费!
⛳ 记住:不从“最左”开始,索引就没你事儿!
九、写在最后
现在你知道为什么加了索引却慢得要命了吧?
很多开发者都误以为“建了索引就无敌”,但真正决定是否使用索引的,恰恰是你怎么写 SQL。
✨ 掌握最左匹配,优化从此不迷路!
如果这篇文章帮到了你,记得点赞 + 收藏 + 转发,救救那些“假装用了索引”的朋友们!