从SQL原本的理解上,不会走联合索引
但是MySQL有优化器,会针对 WHERE b = ? AND a = ? 这种特定情况进行优化,使其走联合索引(a,b)
- 优化器的版本要求 MySQL >= 5.7
- 如果其中有唯一索引,比如a是唯一索引,这里就不会走联合索引了,会走唯一索引,这样更快
在 MySQL 中,联合索引 (a, b) 的查询顺序必须遵循"最左前缀原则" 。如果查询条件是 WHERE b = ? AND a = ?,虽然逻辑上等价于 WHERE a = ? AND b = ?,但能否利用联合索引取决于实际书写顺序和优化器的处理。以下是详细分析:
1. 联合索引 (a, b) 的存储结构
- 索引是按照
(a, b)的顺序存储的,即先按a排序,a相同的情况下再按b排序。 - 最左前缀原则:查询必须从索引的最左列开始,才能有效利用索引。
2. WHERE b = ? AND a = ? 的情况
(1) 能否利用索引?
- 严格来说,不能直接利用
(a, b)索引 ,因为查询条件没有从a开始。 - 但 MySQL 优化器可能会自动重写条件 :
- 如果优化器将
WHERE b = ? AND a = ?重写为WHERE a = ? AND b = ?,则可以利用索引。 - 如果未重写,则无法利用索引,可能导致全表扫描。
- 如果优化器将
(2) 实验验证
表结构与索引
sql
`CREATE TABLE test (
id INT PRIMARY KEY,
a INT,
b INT,
INDEX idx_a_b (a, b)
);`
查询 1:WHERE a = 1 AND b = 2(符合最左前缀)
sql
`EXPLAIN SELECT * FROM test WHERE a = 1 AND b = 2;`
- 结果 :会使用
idx_a_b索引(key: idx_a_b)。
查询 2:WHERE b = 2 AND a = 1(条件顺序相反)
sql
`EXPLAIN SELECT * FROM test WHERE b = 2 AND a = 1;`
- 结果 :
- MySQL 5.7+ 和 8.0 :优化器会自动重写为
WHERE a = 1 AND b = 2,仍然使用idx_a_b。 - 旧版本 MySQL:可能无法利用索引,导致全表扫描。
- MySQL 5.7+ 和 8.0 :优化器会自动重写为
3. 关键结论
- 索引顺序是物理存储顺序,查询必须从最左列开始才能高效利用。
- MySQL 优化器会尝试重写条件 ,因此
WHERE b = ? AND a = ?在大多数现代版本中仍能利用(a, b)索引。 - 但依赖优化器不可靠 ,建议显式写成
WHERE a = ? AND b = ?以确保索引被使用。
4. 其他相关场景
(1) 只查询 b 列(WHERE b = ?)
- 无法利用
(a, b)索引 ,因为缺少a的条件。 - 解决方法:
- 单独为
b创建索引。 - 使用覆盖索引(如
(b, a))。
- 单独为
(2) 范围查询 + 精确匹配
WHERE a = 1 AND b > 10:可以利用(a, b)索引(先定位a = 1,再在范围内找b > 10)。WHERE a > 1 AND b = 10:只能用到a的部分索引 ,因为b的条件在范围查询后无法高效过滤。
5. 最佳实践
- 遵循最左前缀原则:查询条件从索引的最左列开始。
- 显式书写条件顺序 :避免依赖优化器重写,直接写
WHERE a = ? AND b = ?。 - 使用
EXPLAIN验证:检查执行计划是否真的用了索引。 - 考虑索引覆盖 :如果只需查询
a和b,确保索引包含所有需要的列(如INDEX idx_a_b (a, b))。
总结
WHERE b = ? AND a = ?在现代 MySQL 中通常能利用(a, b)索引(因优化器重写)。- 但最安全的方式是显式写成
WHERE a = ? AND b = ?。 - 如果查询不符合最左前缀(如仅
WHERE b = ?),则无法利用(a, b)索引。
建议通过 EXPLAIN 实际验证查询计划!