mysql 索引失效的各种情况总结

本文总结了在数据库查询中导致索引失效的15种常见情况,包括未使用索引列、对索引列进行运算、隐式类型转换等,深入解析如何避免这些情况以提高查询效率。

最近面试经常被问到索引什么条件下失效,这里总结下:

1) 没有查询条件,或者查询条件没有建立索引 

2) 在查询条件上没有使用引导列 

3) 查询的数量是大表的大部分,应该是30%以上。 

4) 索引本身失效

5) 查询条件使用函数在索引列上,或者对索引列进行运算,运算包括(+,-,*,/,! 等) 错误的例子:select * from test where id-1=9; 正确的例子:select * from test where id=10; 

6) 对小表查询 

7) CBO计算走索引花费过大的情况。其实也包含了上面的情况,这里指的是表占有的block要比索引小。 

8)隐式转换导致索引失效.这一点应当引起重视.也是开发中经常会犯的错误. 由于表的字段tu_mdn定义为varchar2(20),但在查询时把该字段作为number类型以where条件传给Oracle,这样会导致索引失效. 错误的例子:select * from test where tu_mdn=13333333333; 正确的例子:select * from test where tu_mdn='13333333333'; 

9) 1,<> 2,单独的>,<,(有时会用到,有时不会) 

10,like "%_" 百分号在前. 

11,单独引用复合索引里非第一位置的索引列. 

12,not in ,not exist. 

13,当变量采用的是times变量,而表的字段采用的是date变量时.或相反情况。 

14,B-tree索引 is null不会走,is not null会走,位图索引 is null,is not null 都会走 

15,联合索引 is not null 只要在建立的索引列(不分先后)都会走, in null时 必须要和建立索引第一列一起使用,当建立索引第一位置条件是is null 时,其他建立索引的列可以是is null(但必须在所有列 都满足is null的时候),或者=一个值; 当建立索引的第一位置是=一个值时,其他索引列可以是任何情况(包括is null =一个值),以上两种情况索引都会走。其他情况不会走。

<think> 我们正在回答用户关于MySQL索引失效情况的问题。根据引用内容,我们可以总结一些常见的索引失效情况,并补充其他常见情况。 注意:引用[1]和引用[2]提供了部分索引失效的场景,我们需要结合这些引用并补充其他常见情况。 常见的索引失效情况包括但不限于以下情况: 1. 使用否定条件:当使用`!=`、`<>`、`NOT IN`、`NOT LIKE`等否定操作符时,索引可能会失效。 示例:`SELECT * FROM orders WHERE status != 'completed';` [^1] 2. 使用函数:在WHERE子句中对索引列使用函数时,索引失效。 示例:`EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE LEFT(student.name,3) = 'abc';` [^2] 但可以优化为使用LIKE:`EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name LIKE 'abc%';`(此时会使用索引)[^2] 3. 使用计算:在WHERE子句中对索引列进行算术运算或其他计算时,索引失效。 示例:`EXPLAIN SELECT SQL_NO_CACHE id, stuno, NAME FROM student WHERE stuno+1 = 900001;` [^2] 4. 类型转换:如果索引列是字符串类型,而查询条件中使用了数字(或者反之),会导致索引失效。 示例:`SELECT * FROM user WHERE phone = 13800138000;`(phone是varchar类型) 5. OR条件使用不当:如果OR条件中有一个列没有索引,那么即使其他列有索引,整个查询也不会使用索引。 示例:`SELECT * FROM table WHERE column1 = 'value' OR column2 = 'value';` 如果column2没有索引,则整个查询不会使用索引。 6. LIKE以通配符开头:当使用LIKE且通配符在开头时,索引失效。 示例:`SELECT * FROM user WHERE name LIKE '%abc';` 7. 复合索引未遵循最左前缀原则:对于复合索引,如果查询条件不是从索引的最左列开始,或者跳过了中间列,索引可能失效。 示例:有复合索引`(a, b, c)`,查询条件为`b=1`或`c=1`,或者`a=1 and c=1`(跳过了b)等。 8. 使用IS NULL或IS NOT NULL:在某些情况下,使用IS NULL或IS NOT NULL可能会导致索引失效。但这取决于表数据的具体情况(例如,如果该列不允许为NULL,则优化器可能使用索引)。 9. 范围条件右边的列:在复合索引中,如果某个列使用了范围条件(比如>、<、between等),则其右边的列无法使用索引。 示例:复合索引`(a, b, c)`,条件`a=1 and b>10 and c=2`,那么c列不能使用索引。 10. 数据分布影响:如果MySQL优化器发现全表扫描比使用索引更快(例如,表中数据量很小,或者满足条件的行数占大部分),则可能不使用索引。 11. 使用IN:在MySQL中,使用IN通常还是会使用索引的,但如果IN中的值过多,优化器可能会认为全表扫描更快,从而不使用索引。不过这不是绝对的失效,需要根据具体情况而定。 12. 使用连接查询时,连接条件字符集不一致:当两个表进行连接时,如果连接条件的字符集不同,会导致索引失效。 我们可以根据这些情况来组织回答,并给出相应的示例和解决方案。 注意:引用[3]是关于EXPLAIN语句结果列的解释,我们在解释索引失效时,可以通过EXPLAIN来查看是否使用了索引(key列显示为NULL表示未使用索引)。 此外,我们还需要生成相关问题。 </think> ### MySQL索引失效的常见情况及解决方案 #### 一、操作符导致失效 1. **否定操作符失效** 在使用`!=`, `<>`, `NOT IN`, `NOT LIKE`等否定条件时,索引通常失效: ```sql -- 索引失效示例(status字段有索引idx_status) SELECT * FROM orders WHERE status != 'completed'; SELECT * FROM orders WHERE status NOT IN ('completed', 'shipped'); -- [^1] ``` **解决方案**: - 改为正向条件:`status IN ('pending', 'processing')` - 使用覆盖索引:`SELECT status FROM orders WHERE status != 'completed'` 2. **范围查询右侧失效** 复合索引中,范围查询(`>`, `<`, `BETWEEN`)之后的列无法使用索引: ```sql -- 复合索引 (a,b,c) SELECT * FROM table WHERE a=1 AND b>10 AND c=2; /* 仅用到a,b */ ``` #### 二、表达式处理导致失效 3. **函数操作失效** 对索引列使用函数会使索引失效: ```sql -- 失效(LEFT函数) SELECT * FROM student WHERE LEFT(name,3) = 'abc'; -- [^2] ``` **解决方案**: ```sql -- 改为LIKE前缀匹配(利用索引) SELECT * FROM student WHERE name LIKE 'abc%'; -- [^2] ``` 4. **计算表达式失效** 索引列参与计算会失效: ```sql -- 失效(stuno字段有索引) SELECT * FROM student WHERE stuno+1 = 900001; -- [^2] ``` **解决方案**: ```sql -- 隔离计算部分 SELECT * FROM student WHERE stuno = 900001 - 1; ``` #### 三、数据结构导致失效 5. **隐式类型转换失效** 数据类型不匹配触发隐式转换: ```sql /* phone是varchar类型 */ SELECT * FROM users WHERE phone = 13800138000; /* 失效 */ ``` **解决方案**: ```sql SELECT * FROM users WHERE phone = '13800138000'; /* 有效 */ ``` 6. **LIKE通配符前缀失效** 前导通配符使索引失效: ```sql SELECT * FROM products WHERE name LIKE '%apple%'; /* 全表扫描 */ ``` **解决方案**: - 使用后缀匹配:`name LIKE 'apple%'` - 添加全文索引:`ALTER TABLE products ADD FULLTEXT(name)` #### 四、优化器决策失效 7. **数据倾斜导致失效** 当满足条件的数据超过总量的$20\% \sim 30\%$时,优化器可能选择全表扫描: ```sql -- 订单表90%状态为'completed' SELECT * FROM orders WHERE status = 'completed'; /* 可能全表扫描 */ ``` **解决方案**: - 强制使用索引:`SELECT * FROM orders FORCE INDEX(idx_status) WHERE ...` - 使用覆盖索引减少IO 8. **OR条件部分失效** 若OR条件中有非索引列,整个查询失效: ```sql -- name有索引,address无索引 SELECT * FROM users WHERE name='Alice' OR address='Beijing'; ``` **解决方案**: ```sql -- 拆分为UNION查询 SELECT * FROM users WHERE name='Alice' UNION ALL SELECT * FROM users WHERE address='Beijing' AND name!='Alice'; ``` #### 五、验证索引使用情况 使用`EXPLAIN`分析执行计划(关键字段): - **possible_keys**:可能使用的索引 - **key**:实际使用的索引(若为NULL则索引失效) - **type**:访问类型(const > ref > range > index > ALL) -- [^3] ![](https://via.placeholder.com/600x200?text=EXPLAIN输出示例图示) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值