Mysql联合索引违反最左匹配,依然走联合索引

创建表t2,设置id字段为主键,给字段 a,b,c 建立联合索引 index_abc:

mysql> create table t2(id int primary key,
    -> a int,
    -> b int,
    -> c int,
    -> index index_abc(a,b,c));

插入数据:

+----+------+------+------+
| id | a    | b    | c    |
+----+------+------+------+
|  0 |    1 |    3 |    2 |
|  1 |    2 |    4 |    3 |
|  2 |    3 |    6 |    4 |
+----+------+------+------+

违反最左匹配,where条件中以字段b开始查询:

mysql> explain select * from t2 where b=1 and c=3;
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys | key       | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | t2    | NULL       | index | index_abc     | index_abc | 15      | NULL |    3 |    33.33 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+

发现mysql依然走了联合索引 index_abc,与联合索引最左匹配原则相悖,那么原因是什么呢?

向表t2中插入一个新字段d:

mysql> alter table t2 add d int;

mysql> desc t2;
+-------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------+------+-----+---------+-------+
| id    | int  | NO   | PRI | NULL    |       |
| a     | int  | YES  | MUL | NULL    |       |
| b     | int  | YES  | MUL | NULL    |       |
| c     | int  | YES  |     | NULL    |       |
| d     | int  | YES  |     | NULL    |       |
+-------+------+------+-----+---------+-------+

在字段d插入数据:

mysql> select * from t2;
+----+------+------+------+------+
| id | a    | b    | c    | d    |
+----+------+------+------+------+
|  0 |    1 |    3 |    2 |    1 |
|  1 |    2 |    4 |    3 |    2 |
|  2 |    3 |    6 |    4 |    3 |
+----+------+------+------+------+

 再次运行这条sql语句:

mysql> explain select * from t2 where b=1 and c=3;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t2    | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+

mysql此时并没有走联合索引 index_abc,而是进行了全表扫描。

总结

如果联合索引包含了所有字段(除了主键),不论是否违反联合索引最左匹配原则,都会走联合索引,反之,查询依然符合联合索引最左匹配原则。

 

<think>我们正在讨论联合索引最左匹配原则。用户要求解释该原则及其违反情况。根据引用,最左匹配原则是指在使用联合索引时,查询必须从索引的最左侧列开始才能有效利用索引。如果跳过最左侧列,索引可能无法被使用。 首先,定义联合索引联合索引是在多个列上创建的索引索引键按照创建时列的顺序存储(例如,索引`(a, b, c)`会先按a排序,a相同再按b排序,b相同再按c排序)。 最左匹配原则的含义: - 查询条件必须包含联合索引的最左侧列(即第一个列),才能使用索引。 - 如果查询条件包含索引的连续左侧列(例如,索引(a,b,c),查询条件包含a和b),则索引可以部分使用使用到a和b)。 - 如果查询条件包含最左侧列,则整个索引无法被使用索引失效)。 违反最左匹配原则的情况(导致索引失效): 1. 查询条件中缺少最左侧列:例如,索引是`(a, b, c)`,但查询条件只有`b=2`或`c=3`,则索引失效。 2. 查询条件跳过中间列:例如,索引`(a, b, c)`,查询条件为`a=1 AND c=3`,此时只能使用到a列索引(因为c列在索引中依赖于b列,而b列缺失导致c在索引中无法被有效使用),c列条件需要回表后过滤。 3. 查询条件中列的顺序与索引列顺序一致:但注意,MySQL的查询优化器可能会调整条件顺序,只要实际包含最左列且条件连续,索引仍可能被使用。但是,如果中间有缺失,则能连续使用。 另外,引用[4]提到一个特殊情况:精确匹配最左列并范围匹配非右一列时,索引可能部分使用。例如,索引`(a,b,c)`,查询`a=1 AND b>2 AND c=3`,此时a和b可以使用索引(因为a是精确匹配,b是范围匹配),但c只能在b>2的结果中过滤(因为b的范围匹配导致c在索引是有序的,所以c条件无法使用索引)。 为了更清晰,我们可以用例子说明: 假设有联合索引`(name, age, city)`。 情况1:遵循最左匹配原则 - `WHERE name='Alice'` → 使用索引使用name列) - `WHERE name='Alice' AND age=25` → 使用索引使用name和age列) - `WHERE name='Alice' AND age=25 AND city='Beijing'` → 使用索引(全部使用) 情况2:违反最左匹配原则(索引失效或部分失效) - `WHERE age=25` → 索引失效(缺少最左列name) - `WHERE age=25 AND city='Beijing'` → 索引失效(缺少最左列name) - `WHERE name='Alice' AND city='Beijing'` → 索引部分使用(只能使用name列,city条件需要回表后过滤) - `WHERE city='Beijing'` → 索引失效 情况3:范围查询后的列无法使用索引 - `WHERE name='Alice' AND age>20 AND city='Beijing'` → 索引使用到name和age,但city无法使用索引(因为age是范围查询,导致city在索引是有序的),所以city条件只能在满足name和age条件的记录中回表后过滤。 总结: 1. 最左匹配原则要求查询条件必须包含联合索引的最左列,并且尽可能连续(跳过中间列)。 2. 违反最左匹配原则会导致索引失效(完全失效或部分失效),从而可能引起全表扫描或回表大量数据,降低查询性能。 根据引用[5]的解释,索引失效的原因是联合索引的B+树结构是按照索引定义的列顺序构建的,非叶子节点和叶子节点都是按照这个顺序排序。如果查询条件包含最左列,数据库无法利用B+树的有序性快速定位数据。 最后,生成相关问题。</think>### 联合索引最左匹配原则详解 #### 一、核心定义 **最左匹配原则**(Leftmost Prefix Rule)是联合索引的核心使用规则: > **当查询使用联合索引时,必须从索引的最左侧列开始匹配才能生效**。联合索引的物理存储按列顺序排序(如索引`(a,b,c)`,数据先按`a`排序,`a`相同再按`b`排序,最后按`c`排序)[^2][^3]。 #### 二、工作原理(以索引`(a,b,c)`为例) ```mermaid graph LR A[联合索引 a,b,c] --> B[存储结构] B --> C1[a有序] C1 --> C2[a相同则b有序] C2 --> C3[a,b相同则c有序] ``` 1. **有效使用索引的场景**: - ✅ `WHERE a=1` → 使用`a`列索引 - ✅ `WHERE a=1 AND b=2` → 使用`a,b`列索引 - ✅ `WHERE a=1 AND b=2 AND c=3` → 使用完整索引 2. **索引部分生效的场景**: - ⚠️ `WHERE a=1 AND c=3` → **仅使用`a`列索引** *原因*:缺失`b`导致`c`在索引中无序,需回表过滤`c=3`[^4][^5] 3. **索引完全失效的场景**: - ❌ `WHERE b=2` → **全表扫描** *原因*:未指定最左列`a`,无法利用索引排序[^2][^5] - ❌ `WHERE c=3` → **全表扫描** - ❌ `WHERE b=2 AND c=3` → **全表扫描** --- ### 三、违反最左匹配原则的典型情况 #### 1. 跳过最左列(索引完全失效) ```sql -- 索引(a,b,c) SELECT * FROM table WHERE b=10; -- 索引失效[^5] ``` *原理*: B+树未对`b`单独全局排序,只能遍历所有叶子节点(全表扫描)。 #### 2. 中断连续匹配索引部分失效) ```sql -- 索引(a,b,c) SELECT * FROM table WHERE a=1 AND c=5; -- 仅用a列索引[^4] ``` *原理*: `a=1`时`c`的值可能无序(因依赖`b`排序),需回表逐行过滤`c=5`[^5]。 #### 3. 范围查询中断匹配 ```sql -- 索引(a,b,c) SELECT * FROM table WHERE a>2 AND c>3; -- 仅用a列索引 ``` *原理*: `a>2`返回的多个数据块中,`c`值无序(因`b`未被限定),无法使用`c`索引[^4]。 --- ### 四、特殊场景分析 #### 场景1:乱序条件但包含最左列 ```sql -- 索引(a,b,c) SELECT * FROM table WHERE b=2 AND a=1; -- ✅ 索引生效 ``` *说明*: 查询优化器会自动调整条件顺序为`a=1 AND b=2`[^3]。 #### 场景2:最左列范围查询 + 精确匹配 ```sql -- 索引(a,b,c) SELECT * FROM table WHERE a>1 AND b=2; -- ⚠️ 仅用a列索引 ``` *原理*: `a>1`时`b`的值可能无序(因`a`同),需回表过滤`b=2`。 --- ### 五、优化建议 1. **索引设计**: 将高频查询列作为联合索引的最左列[^1][^3]。 2. **查询改写**: ```sql -- 低效: WHERE b=2 -- 高效: WHERE a=[固定值] AND b=2 ``` 3. **覆盖索引**: ```sql -- 避免回表 SELECT a,b FROM table WHERE a=1 AND b=2; -- 索引覆盖 ``` > 📊 **性能对比**: > | 查询条件 | 索引使用情况 | I/O类型 | > |-------------------|------------------|---------------| > | `a=1 AND b=2` | 完整索引 | 顺序I/O | > | `a=1 AND c=3` | 仅`a`列索引 | 随机I/O | > | `b=2` | 索引失效 | 全表扫描 | --- ### 六、常见误区 ❌ **误区**:`WHERE b=2 AND a=1`违反最左匹配 ✅ **真相**:优化器会重写为`a=1 AND b=2`,索引仍生效[^3]。 ❌ **误区**:联合索引等效于多个独立索引 ✅ **真相**:索引`(a,b)` ≠ 索引`(a)` + 索引`(b)`,前者支持组合查询[^1][^2]。 --- ### 相关问题 1. **为什么范围查询(如`a>1`)会导致后续索引列失效?**[^4] 2. **如何设计联合索引以优化`WHERE a=? AND c=?`类查询?** 3. **覆盖索引如何避免回表查询?** 4. **联合索引的列顺序对排序操作(`ORDER BY`)有何影响?** > 引用文献: > [^1]: 最左匹配原则基础定义与性能影响 > [^2]: 联合索引存储结构与查询逻辑 > [^3]: 条件顺序调整与索引生效机制 > [^4]: 范围查询导致索引部分失效原理 > [^5]: 违反最左匹配索引失效分析
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值