mysql 多重索引_where条件中出现了(不等于/多重范围)就一定不走索引了吗

本文探讨了MySQL中不等于条件和多重范围在索引使用上的情况。通过各种查询改写和实验,得出结论:在覆盖索引情况下,不等于条件仍可能走索引;多重范围的第一个范围会走索引,后续范围用于过滤;非覆盖索引时,不等于通常不走索引,优化器会权衡是否使用部分索引。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天朋友抛了一个群里的讨论给我,如下图

91cd450bdf5c?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

这题我选择了A,B,因为我认为A和B都不满足最左;最后朋友实际建表的测试结果如下(也就是说答案应该是A,B,D)

91cd450bdf5c?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

按理说不等于不走索引应该是SQL优化的常识了,为什么我在选择的时候排除了D呢?因为2个月前我遇到了这样一个语句

SELECT MAX(id)

FROM a

WHERE user_id != '18'

AND '2019-02-13 00:00:00' >= create_time

GROUP BY user_id;

91cd450bdf5c?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

因为这个语句的关系,我一度认为在5.7.25版本里不等于也已经开始智能的走索引了;经过这个选择题,决定仔细探索一下MySQL的不等于

对原语句进行不同的改写

备注:

①索引idx_uid_ct为user_id和create_time的联合索引idx_uid_ct(user_id,create_time)

②后面为了测试,特意删除了idx_ct(create_time)

③id为表的主键列

eg1.去除group by的影响,type为range,覆盖索引

91cd450bdf5c?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

eg2.去除max函数,type为range,覆盖索引

91cd450bdf5c?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

eg3.查询项改为create_time、user_id、id中的任意1到3个,type为range,覆盖索引

91cd450bdf5c?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

eg4.查询项改为*或者是索引列(user_id,create_time,id)之外的任意值,索引前导列存在不等于,不走索引

91cd450bdf5c?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

eg5.查询项改为*或者是索引列(user_id,create_time,id)之外的任意值,索引的非前导列存在不等于,走索引,extra为ICP

91cd450bdf5c?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

eg6.查询项改为create_time或者user_id,where条件改为只有非前导列create_time,不满足idx_uid_ct最左匹配规则,type为index,覆盖索引

91cd450bdf5c?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

eg7.查询项改为索引列(user_id,create_time,id)之外的任意值,where条件将user_id的不等于改为等于,create_time条件不变,type为range,Extra为ICP

91cd450bdf5c?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

eg8.查询项改为索引列(user_id,create_time,id)之外的任意值,where条件将user_id的不等于改为大于或者小于,create_time条件不变,是否走索引需要优化器进行判断

91cd450bdf5c?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

91cd450bdf5c?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

eg9.查询项改为索引列(user_id,create_time,id)之中的任意1-3个值,where条件将user_id的不等于改为大于或者小于,create_time条件不变,type为range,覆盖索引

91cd450bdf5c?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

结论

①对比上面的9个eg可以看出,当查询列位于主键或者索引列范围内的时候(或者像原语句那样是主键/索引列的分组函数),整个语句会直接走覆盖索引,这种情况下无论where条件里有没有不等于都不会有影响

②参考eg4可知查询列在索引列或主键列的范围之外,索引前导列存在不等于,不走索引

③参考eg5可知,查询列在索引列或主键列的范围之外,索引非前导列存在不等于,仍然走索引,此时需要回表,因此extra为ICP。

④对比eg5和eg7,两者的执行计划中的extra均为ICP,但key_len和type不同;eg5的非前导列在where条件中存在不等于,因此联合索引中的非前导列create_time并没有被利用到,所以key_len只有99;eg7的非前导列条件where条件中是一个范围,由于是where条件中的第一个范围,因此这个范围可以走索引,所以key_len为105;eg5和eg7之所以都是ICP是因为查询列user_name需要回表

⑤参考eg9,当满足覆盖索引条件的时候,即使在where条件中有双范围,第一个范围走了索引,第二个范围没有走索引(key_len为99)

⑥参考eg8,不满足覆盖索引条件时,where条件中只有第一个范围能被索引利用到,是否走索引优化器自身会做权衡,当取值量太大时索引反而不如全表

⑦eg6是一个我不能理解的eg,满足覆盖索引条件时,即使where条件不满足索引的最左规则,语句仍然走了索引只不过type为极为低效的index(全索引扫描),extra显示此时仍然是覆盖索引;有趣的是这里的key_len竟然是105,而实际上where条件里面只有create_time(该列的key_len应该只有6才对)

综上可以知道在覆盖索引的情况下,不等于仍然会走索引(无论这个不等于是否存在于前导列的where条件上),而多重范围的第一个范围会走索引,而后面的多重范围则不会走索引,只是用来作为过滤条件使用;

在非覆盖的索引下,不等于不会走索引(如果是前导列存在不等于,这个索引直接不会走;如果是非前导列存在不等于,这个索引有可能被用到,但即使索引被用到,索引中这个存在不等于的列也不会被用到),而多重范围的后几层范围肯定不会被用到,第一层范围是否会用到需要优化器进行权衡

补充

SELECT sum( amount ) FROM t

WHERE

STATUS = 'success'

AND partner_id != 'aaa' AND partner_id != 'bbb' AND partner_id != 'ccc'

AND date_format( update_time, '%y-%m-%d' ) = date_format( now( ), '%y-%m-%d' );

建立测试索引

idx_0(status,partner_id,update_time,amount)

idx_1(status,partner_id,amount)

①在采用idx_1时,索引只走了idx_1中的status部分,这时候存在不等于条件的partner_id只是起到了过滤的作用(这个过滤作用使得该语句执行速度提升10倍)

②在采用idx_0时,索引走了idx_0中的status和partner_id部分,这时候因为已经是覆盖查询了,根据前面的实验可以知道不等于也走了索引,但是最末端的update_time仍然没有走索引,而只是起到了过滤的作用(这个过滤作用使得该语句在之前提升10倍的基础上再提升了10倍)

结论

在覆盖索引的情况下,不等于会走索引,多重范围不会走索引,函数也不会走索引

<think>嗯,用户问的是当MySQL的查询条件中没有包含索引列时,会索引。这个问题看起来挺直接的,但需要仔细想想。首先,我得回忆一下索引的基本原理。索引的作用是帮助数据库快速定位数据,所以如果查询条件里没有索引列,那数据库还能用上索引吗? 可能用户遇到的情况是,他们写了一个查询,但是发现性能好,怀疑是不是因为没有用到索引。或者他们在设计表结构的时候,想了解如何合理使用索引。这时候需要明确,没有索引列的查询条件,数据库会会使用索引。 首先,应该明确的是,如果WHERE子句中的条件没有涉及到任何索引列,那么MySQL通常无法使用这些索引来加速查询。这时候查询会进行全表扫描,也就是逐行检查每一行是否符合条件,这显然效率高,尤其是在数据量大的情况下。 过,有没有例外情况呢?比如覆盖索引的情况。覆盖索引指的是查询的列都包含在某个索引中,即使WHERE条件没有使用索引列,但SELECT的列都在索引里,这时候可能会使用索引来避免回表。但仔细想想,覆盖索引的使用前提是查询可以通过索引直接获取数据,而如果WHERE条件没有用到索引,那么即使SELECT的列在索引里,MySQL还是需要扫描整个索引,这时候可能和全表扫描差多,甚至更差,因为索引的大小可能比数据行小,但也要看具体情况。 另外,用户可能还太清楚索引的类型,比如主键索引、唯一索引、普通索引等。主键索引是特殊的,如果查询没有WHERE条件,但需要返回所有数据,这时候即使没有条件,如果有ORDER BY主键的情况,可能还是会用主键索引来排序。但如果没有WHERE条件,查询所有数据的话,无论有没有索引,都是全表扫描,除非使用覆盖索引索引包含了所有需要的列,这时候可能扫描索引不是数据行。 还有一种情况是使用索引合并,但这种情况需要WHERE条件中有多个索引列的组合,如果查询条件中没有任何索引列,那索引合并也起作用。 再考虑一些特殊情况,比如使用LIMIT,当查询没有WHERE条件但使用了LIMIT时,如果存在合适的索引MySQL可能会选择扫描索引来快速找到符合的行,但这种情况还是依赖于具体的查询和索引结构。 用户的问题可能还涉及到EXPLAIN命令的使用。通过EXPLAIN可以查看查询的执行计划,确认是否使用了索引。如果用户没有使用索引列,EXPLAIN的结果中的key字段会是NULL,表示没有使用索引。 另外,用户可能有误解,认为索引的存在会影响所有查询,但实际上索引只有在查询条件、排序或连接条件中使用到相关列时才会被考虑。所以需要向用户明确,索引的使用是有条件的,不是所有查询都能受益于索引。 最后,总结一下:当查询条件中没有包含任何索引列时,MySQL通常会使用索引,而是进行全表扫描。但如果有覆盖索引的情况,可能会扫描索引不是数据行,但这仍然属于全表扫描的一种形式,只是扫描的对象同。因此,在设计查询时,尽量在WHERE条件中使用索引列,以提高查询效率。</think>当MySQL查询的**WHERE条件包含任何索引列**时,数据库通常**会使用索引**,而是执行全表扫描(Full Table Scan)。以下是具体分析及例外场景说明: --- ### 一、基础结论 1. **常规情况**: - 如果查询条件中没有涉及任何索引列(包括主键、普通索引等),MySQL优化器无法通过索引快速定位数据,必须逐行检查所有数据行,即**全表扫描**。 - 示例: ```sql -- 假设name列无索引,age列无索引 SELECT * FROM users WHERE name = 'John' AND age > 25; -- ✖️ 全表扫描 ``` --- ### 二、例外场景(可能间接使用索引) #### 1. **覆盖索引(Covering Index)** - **场景**:若查询的**所有字段**都包含在某个索引中(即使WHERE条件未使用该索引),MySQL可能直接扫描索引树(而非数据行),称为“**索引覆盖扫描**”。 - **示例**: ```sql -- 假设存在索引 idx_age_name (age, name) SELECT name FROM users WHERE gender = 'M'; -- ✖️ WHERE条件索引列 -- ✔️ 但SELECT的name在索引中 ``` - 此时会扫描整个`idx_age_name`索引(比全表扫描快,但仍是遍历索引)。 #### 2. **排序/分组优化** - **场景**:若查询包含`ORDER BY`或`GROUP BY`且相关字段有索引,即使WHERE条件索引,可能使用索引排序。 - **示例**: ```sql -- 假设存在索引 idx_age (age) SELECT * FROM users WHERE gender = 'F' ORDER BY age; -- ✖️ WHERE索引 -- ✔️ 但排序用到了idx_age ``` - 优化器可能选择:**全表扫描后通过索引排序**(而非直接遍历索引)。 #### 3. **LIMIT优化** - **场景**:当查询包含`LIMIT N`且数据分布有利时,优化器可能优先扫描主键索引(聚簇索引)快速定位前N行。 - **示例**: ```sql SELECT * FROM users WHERE gender = 'M' LIMIT 10; -- ✖️ WHERE索引 -- ✔️ 可能直接扫描主键索引 ``` --- ### 三、验证方法 通过`EXPLAIN`命令观察执行计划: 1. **全表扫描标志**: - `type = ALL` - `key = NULL`(未使用索引) ```sql EXPLAIN SELECT * FROM users WHERE gender = 'M'; ``` **结果示例**: ``` +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | users | ALL | NULL | NULL | NULL | NULL | 1000 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ ``` 2. **覆盖索引扫描标志**: - `type = index` - `Extra = Using index` ```sql EXPLAIN SELECT name FROM users WHERE gender = 'M'; ``` **结果示例**: ``` +----+-------------+-------+-------+---------------+--------------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+--------------+---------+------+------+--------------------------+ | 1 | SIMPLE | users | index | NULL | idx_age_name | 68 | NULL | 1000 | Using where; Using index | +----+-------------+-------+-------+---------------+--------------+---------+------+------+--------------------------+ ``` --- ### 四、优化建议 1. **核心原则**: - 确保查询条件中至少包含一个索引列(尤其是高频查询)。 - 对常作为查询条件的字段建立索引。 2. **覆盖索引扩展**: - 若需频繁查询某些非索引字段(如`SELECT email FROM users WHERE gender='F'`),可创建联合索引`(gender, email)`,使查询完全通过索引完成。 3. **统计信息更新**: - 定期执行`ANALYZE TABLE table_name`,确保优化器准确评估索引效率。 --- ### 五、总结 | **场景** | **是否使用索引** | **性能影响** | |-------------------------|------------------|---------------------------| | WHERE条件索引列 | 否(全表扫描) | 数据量大时性能差 | | 覆盖索引扫描 | 是(遍历索引) | 比全表扫描快,但仍需遍历 | | 排序/分组使用索引 | 部分使用 | 减少排序时间,但需全表扫描 | **结论**:查询条件索引列时,MySQL通常无法有效利用索引。唯一例外是覆盖索引或特定排序场景,但这仍属于“全索引扫描”而非“索引查找”。合理设计索引仍是优化查询性能的关键。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值