SQL索引失效案例

本文详细分析了MySQL中导致索引失效的11种常见情况,包括全值匹配、最左前缀原则、主键插入顺序、计算和函数、类型转换、范围条件、不等于操作、IS NULL与IS NOT NULL、LIKE通配符、OR条件和字符集问题。理解这些情况有助于优化SQL查询,提高数据库性能。

索引失效案例

MysQL中提高性能的一个最有效的方式是对数据表设计合理的索引。索引提供了高效访问数据的方法,并且加快查询的速度,因此索引对查询的速度有着至关重要的影响。

  • 使用索引可以快速地定位.表中的某条记录,从而提高数据库查询的速度,提高数据库的性能。
  • 如果查询时没有使用索引,查询语句就会扫描表中的所有记录。在数据量大的情况下,这样查询的速度会很慢。
    大多数情况下都(默认)采用B+树来构建索引。只是空间列类型的索引使用R-树,并且MEMORY表还支持hash索引。l

其实,用不用索引,最终都是优化器说了算。优化器是基于什么的优化器?基于cost开销
(CostBaseOptimizer),它不是基于规则(Rule-Based0ptimizer),也不是基于语义。怎么样开销小就怎么来。另外,SQL语句是否使用索引,跟数据库版本、数据量、数据选择度都有关系。

1.1 全值匹配

有几个where过滤条件,就建立几个索引,效率会逐步提升

1.2 最左前缀原则

在建立联合索引时

数据库会根据联合索引中字段顺序依次建立B+树

比如下面这条建立索引的语句

CREATE INDEx idx_age_classid_name oN student (age,classId,NAME);

会先根据age顺序建立B+树,当age值相同时,再根据classId的顺序建立B+树,NAME字段同理

同时再进行and操作时,MySQL的优化器会调整and语句的执行顺序

只要是and语句中有以联合索引最左边的字段为查询条件时,都会走该联合索引

在这里插入图片描述

第一条语句走第一个索引

不走第三个索引是因为,第三个B+树是依次根据age,classID,NAME的顺序建立的B+树,age字段可以走该索引,但NAME字段不能走该索引

因为建立索引的循序是age,classID ,NAME ,只有classId字段走了索引之后,NAME字段才能走索引

但是第一条查询语句中只有age,name两个字段作为查询条件,所以name根本走不了索引,即使该句走了第三个索引,本质上还是走的第一个索引,即使以age字段建立的B+树

结论:MysQL可以为多个字段创建索引,一个索引可以包括16个字段对于多列索引,过滤条件要使用索引必须按照索引建立时的顺序,依次满足,一旦跳过某个字段,索引后面的字段都无法被使用。用这些字段中第1个字段时,多列(或联合)索引不会被使用。

1.3 主键插入顺序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t0qX7Thj-1666309824781)(C:\Users\wu\AppData\Roaming\Typora\typora-user-images\image-20220925224001185.png)]

可这个数据页已经满了,再插进来咋办呢?我们需要把当前 页面分裂 成两个页面,把本页中的一些记录 移动到新创建的这个页中。页面分裂和记录移位意味着什么?意味着: 性能损耗 !所以如果我们想尽量 避免这样无谓的性能损耗,最好让插入的记录的 主键值依次递增 ,这样就不会发生这样的性能损耗了。所以我们建议:让主键具有 AUTO_INCREMENT,让存储引擎自己为表生成主键,而不是我们手动插入 ,

我们自定义的主键列 id 拥有 AUTO_INCREMENT 属性,在插入记录时存储引擎会自动为我们填入自增的 主键值。这样的主键占用空间小,顺序写入,减少页分裂

1.4 计算、函数、类型转换(自动或手动)导致索引失效

此时的筛选条件是字段经过函数,计算处理后的数据与目标数据相比较

字段本身的数据的顺序此时变得没那么重要了

由于MySQL也不知道什么样的数据再经过计算,函数后会达到条件

他只能一条条的检索全部数据,然后做函数运算。

所以不会走索引

1.5 类型转换导致索引失效

1.6 范围条件右边的列索引失效

原因:想B+树构建,查询过程

此时只有,age,classId两个索引生效了,name并没有生效

这是由于对classId的筛选是一个范围查询,范围条件右边的列索引会失效

所以这提醒我们再建立索引时要将等值条件相关的字段提前,将范围查询的字段置后

1.7 不等于(!= 或者<>)索引失效

原因:想B+树构建,查询过程

1.8 is null可以使用索引,is not null无法使用索引

结论:最好在设计数据表的时候就将字段设置为NOT NULL约束,比如你可以将INT类型的字段,默认值设置为o。将字符类型的默认值设置为空字符串()。
拓展:同理,在查询中使用not like 也无法使用索引,导致全表扫描。

1.9 like以通配符%开头索引失效

1.10 OR 前后存在非索引的列,索引失效

原因:or是只要一个条件满足即可,即便你age有索引,查找age时使用了索引,但这会导致有部分数据没有扫描到,那没有扫描到的情况可能会有classid=100的情况。所以你仍要以classid再再次扫描表,但是classid没有索引,所以需要全表扫描,所以为什么不直接全表扫描呢?

1.11 数据库和表的字符集统一使用utf8mb4

统一使用utf8mb4( 5.5.3版本以上支持)兼容性更好,统一字符集可以避免由于字符集转换产生的乱码。不 同的 字符集 进行比较前需要进行 转换 会造成索引失效。

<think>好的,我现在要解决用户关于如何预防SQL索引失效以及常见原因分析的问题。首先,我需要回忆一下SQL索引的基本原理。索引的作用类似于书籍的目录,帮助数据库快速定位数据。如果索引失效,查询就会变慢,特别是在大数据量的情况下,影响更明显。 接下来,常见原因方面,用户可能遇到的情况包括:在WHERE子句中对索引列使用函数或运算,比如`WHERE YEAR(create_time) = 2023`,这会导致索引失效,因为数据库无法直接使用索引的值。还有类型转换的问题,比如字符串字段用数字查询,或者隐式类型转换,这会让数据库无法有效使用索引。此外,使用LIKE以通配符开头,比如`LIKE '%abc'`,也会让索引失效,因为B树索引的结构是从左到右匹配的。 联合索引的最左前缀原则也是常见的问题点。如果查询条件包含联合索引的最左列,索引就无法被使用。例如,联合索引是`(a, b, c)`,但查询只用了b和c,这时索引可能会被用到。还有OR操作符,如果OR两侧的列是都有索引,可能导致全表扫描。 然后是预防的最佳实践。需要确保WHERE条件中的列避免使用函数或运算,保持列“干净”。类型匹配也很重要,确保查询条件的类型与列定义一致。对于LIKE查询,尽量避免前置通配符,或者考虑使用全文索引。设计联合索引时,按照最左前缀原则安排列的顺序,并且根据查询频率调整。使用EXPLAIN分析执行计划,查看索引使用情况,这是实际优化中常用的方法。另外,定期维护索引,比如重建或优化碎片化的索引,保持统计信息准确,帮助优化器做出正确决策。 还要考虑其他因素,比如索引选择性。选择性低的索引(如性别字段)可能会被使用,这时候可能需要结合其他列建立复合索引。避免过多的索引,因为增删改操作需要维护索引,影响性能。覆盖索引可以减少回表,提高效率,但需要权衡空间和查询需求。 用户可能还关心具体案例,比如如何改写查询以避免函数使用,或者如何分析执行计划。这时候需要给出示例,比如将`YEAR(create_time)=2023`改为范围查询,或者使用EXPLAIN命令查看索引使用情况。 最后,需要确保引用相关的资料,比如用户提到的参考文章,可能涉及更详细的优化策略,这里适当引用,增强回答的可信度。同时,生成的相关问题要围绕索引优化,比如失效场景分析、执行计划解读、联合索引设计等,帮助用户进一步学习。</think>### 预防SQL索引失效的最佳实践与常见原因分析 #### 一、索引失效的常见原因 1. **对索引列进行运算或函数操作** ```sql SELECT * FROM users WHERE YEAR(create_time) = 2023; -- 索引失效 ``` 若对索引列`create_time`使用`YEAR()`函数,数据库无法直接使用索引,需全表扫描[^1]。 2. **隐式类型转换** ```sql SELECT * FROM orders WHERE order_no = 10086; -- 若order_no是字符串类型,索引失效 ``` 数据库需将`order_no`隐式转换为数字,导致索引失效。 3. **LIKE查询以通配符开头** ```sql SELECT * FROM products WHERE name LIKE '%apple%'; -- 无法使用索引 ``` B树索引无法匹配前缀模糊查询,建议使用`LIKE 'apple%'`或全文索引。 4. **未遵循最左前缀原则** 联合索引`(a, b, c)`的查询条件若缺少`a`,如`WHERE b=1 AND c=2`,索引失效。 5. **OR条件未完全覆盖索引** ```sql SELECT * FROM logs WHERE id = 100 OR content = 'error'; -- 若content无索引,全表扫描 ``` --- #### 二、预防索引失效的最佳实践 1. **保持WHERE条件列“干净”** 避免对索引列进行运算或函数调用,改为直接比较: ```sql SELECT * FROM users WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31'; ``` 2. **严格匹配数据类型** 确保查询条件类型与列定义一致: ```sql SELECT * FROM orders WHERE order_no = '10086'; -- 显式使用字符串 ``` 3. **优化LIKE查询结构** 使用右模糊查询或全文索引: ```sql SELECT * FROM products WHERE name LIKE 'apple%'; -- 使用索引 ``` 4. **联合索引设计原则** - 高频查询条件放在最左侧 - 根据查询场景调整列顺序 示例:联合索引`(country, city, age)`可支持以下查询: ```sql SELECT * FROM users WHERE country='中国' AND city='北京'; -- 使用索引 SELECT * FROM users WHERE country='中国' AND age>25; -- 部分使用索引 ``` 5. **使用EXPLAIN分析执行计划** ```sql EXPLAIN SELECT * FROM orders WHERE total_price > 1000; ``` 查看`key`列确认是否命中索引,`rows`列估算扫描行数。 6. **定期维护索引** ```sql ANALYZE TABLE orders; -- 更新统计信息 OPTIMIZE TABLE orders; -- 重建表及索引 ALTER INDEX idx_name REBUILD; -- 重建特定索引(部分数据库支持) ``` --- #### 三、进阶优化策略 1. **覆盖索引优化** 包含查询所需全部字段的索引可避免回表操作: ```sql CREATE INDEX idx_covering ON orders (user_id, status) INCLUDE (total_price); ``` 2. **索引选择性评估** 选择性计算公式: $$ \text{选择性} = \frac{\text{同值数量}}{\text{总行数}} $$ 选择性>0.1的列更适合建索引。 3. **避免过度索引** 每个额外索引会增加写操作成本,建议单表索引超过5个。 --- #### 四、典型案例分析 **案例1:日期范围查询优化** ❌ 低效写法: ```sql SELECT * FROM sales WHERE DATE_FORMAT(sale_time, '%Y-%m') = '2023-08'; ``` ✅ 优化方案: ```sql SELECT * FROM sales WHERE sale_time >= '2023-08-01' AND sale_time < '2023-09-01'; ``` **案例2:组合索引优化** 表结构:`employees (id, dept_id, salary)` 高频查询: ```sql SELECT * FROM employees WHERE dept_id=5 ORDER BY salary DESC LIMIT 10; ``` 优化方案: ```sql CREATE INDEX idx_dept_salary ON employees (dept_id, salary DESC); ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值