避免索引失效的一些原则

本文详细介绍了SQL索引的优化策略,包括复合索引的合理使用、避免索引失效的操作、LIKE语句的优化、类型转换的影响、OR运算符的处理、EXISTS与IN的选择、ORDER BY的优化技巧,以及如何利用慢查询日志进行SQL排查。

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

1.复合索引,不要跨列或无序使用,否则会使组合索引失效
复合索引,尽量使用全索引匹配

2.不要在索引上进行任何操作(计算、函数、类型转换),否则索引失效

EXPLAIN SELECT * FROM book WHERE authorid = 1 AND typeid = 2;
//用到了at2个索引
EXPLAIN SELECT * FROM book WHERE authorid = 1 AND typeid*2 = 2;
//用到了a1个索引
EXPLAIN SELECT * FROM book WHERE authorid*2 = 1 AND typeid*2 = 2;
//用到了0个索引
EXPLAIN SELECT * FROM book WHERE authorid*2 = 1 AND typeid = 2;
//用到了0个索引
//原因:对于复合索引,左边失效了右边全部失效。(a,b,c)中b失效,那b,c都失效,a没有失效。(如果不是复合索引,则没有关系)

在这里插入图片描述

DROP INDEX idx_atb ON book;
ALTER TABLE book ADD INDEX idx_authroid(authroid);
ALTER TABLE book ADD INDEX idx_typeid(typeid);
EXPLAIN SELECT * FROM book WHERE authorid*2 = 1 AND typeid = 2;

在这里插入图片描述
3.复合索引不能使用不等于(!=或<>)或is null(is not null),否则自身以及右侧索引全部失效。
EXPLAIN SELECT * FROM book WHERE authorid = 1 AND typeid = 2;
在这里插入图片描述
只用了一个索引,原因:
–SQL优化,是一种概率层面的优化。至于是否实际使用了我们的优化,需要通过explain进行推测
EXPLAIN SELECT * FROM book WHERE authorid != 1 AND typeid = 2;
在这里插入图片描述
上面的结论是针对复合索引,对单独的索引没有上述影响。

体验概率情况:
  原因是服务层中有SQL优化器,可能影响我们的优化。

show index from book;
drop index idx_typeid on book;
drop index idx_authorid on book;
alter table book add index idx_book_at(authorid,typeid);

在这里插入图片描述

explain select * from book where authorid = 1 and typeid = 2;
//复合索引全部使用,不同于单独索引
explain select * from book where authorid >1 and typeid = 2;
//复合索引中如果有大于号,则自身和右侧索引全部失效。
explain select * from book where authorid =1 and typeid > 2;
//复合索引at全部使用(**体验概率**情况)

在这里插入图片描述
—明显的概率问题—

explain select * from book where authorid < 1 and typeid = 2;//复合索引at中只用到了一个索引
explain select * from book where authorid < 4 and typeid = 2;//复合索引at全部失效

在这里插入图片描述
  明显发现改个数字索引的使用情况都不一样,说明sql优化索引的情况是个概率问题,我们用的优化手段通常都是大部分适用的结论,但由于sql优化器的原因,该结论不是100%正确。
一般而言,范围查询(> < in),之后的索引失效。

4.思考:那到底有没有100%使用索引的情况呢?
答案:有,索引覆盖(using index)—尽量使用索引覆盖

5.like尽量以“常量”开头,不要以‘%’开头,否则索引失效。

select * from xx where name like '%x%';//name索引失效
show index from teacher;

explain select * from teacher where tname like '%x%';//tname索引失效
explain select * from teacher where tname like 'x%';//tname索引有效
explain select tname from teacher where tname like '%x%';//如果必须使用like '%x%',可以使用索引覆盖,挽救一部分(range->index)。

在这里插入图片描述

6.尽量不要使用类型转换(显示、隐式),否则索引失效。

desc teacher;//tname是字符串
show index from teacher;//tname是索引
explain select * from teacher where tname = 'abc';
explain select * from teacher where tname = 123;//程序将123->'123',即进行了类型转换,因此索引失效

在这里插入图片描述
7.尽量不要使用or,否则索引失效

explain select * from teacher where tname = '' and tcid > 1;
explain select * from teacher where tname = '' or tcid > 1;//甚至让左边的索引失效

在这里插入图片描述
8.一些其他的优化方法
(1)exist和in
select … from table exist/in (子查询);
如果主查询的数据集大,则使用in,效率高。
如果子查询的数据集大,则使用exist,效率高。
exist语法
  将主查询的结果,放到子查询结果中进行条件校验(看子查询是否有数据与主查询结果相对应,如果有数据则校验成功),如果符合校验,则保留数据。

select tname from teacher where exists (select * from teacher);
//这里子查询中就有主查询的结果
//等价于select tname from teacher;
select tname from teacher where exists (select * from teacher where tid = 999);
//没有数据

在这里插入图片描述
(2)order by优化—using firesort
有两种算法:双路排序、单路排序(根据IO的次数)
MySQL4.1之前,默认使用双路排序
  双路:扫描两次磁盘(1.从磁盘读取排序字段,对排序字段进行排序(在buffer(缓存区)中进行排序);2.扫描其他字段)。IO较消耗性能
MySQL4.1之后,默认使用
单路排序

  只读取一次(全部字段),在buffer中进行排序。但这单路排序会有一定的隐患(不一定真的是“单路 1次IO”,有可能多次IO—原因:如果数据量特别大,则无法将所有字段的数据一次性读取完毕,因此会进行“分片读取、多次读取”)。

注意:单路排序比双路排序会占用更多的buffer。
  单路排序在使用时,如果数据量大,可以考虑调大buffer的容量大小:set max_length_for_sort_data = 1024 单位byte
如果max_length_for_sort_data值太低,则MySQL会自动从单路->双路(太低:需要排序的列的总大小超过了max_length_for_sort_data定义的字节数)

提高order by查询的策略:
a.选择使用单路、多路;调整buffer的容量大小;
b.避免select * …(->程序还要去计算表示哪些字段)
c.复合索引不要跨列使用,避免using firesort
d.保证全部的排序字段 排序的一致性(都是升序或降序)

9.SQL排查 —慢查询日志
  MYSQL提供的一种日志记录,用于记录MySQL中响应时间超过阈值的SQL语句(long_query_time,默认10秒)—一个sql10秒都没查询出来,就是慢sql,需要优化。
慢查询日志默认时关闭的;建议:开发调优时打开,而最终部署时关闭。
检查是否开启了慢查询日志:show variables like ‘%slow_query_log%’;
临时开启:set global slow_query_log = 1;—在内存中开启
exit;
service mysql restart;
show variables like ‘%slow_query_log%’;—off
永久开启:
/etc/my.cnf中追加配置:
vim /etc/my.cnf
[mysqld]
slow_query_log=1
slow_query_log_file=/var/lib/mysql/localhost-slog.log
慢查询阈值:
show variables like ‘%long_query_time%’;
临时设置阈值:
set global long_query_time = 5;—设置完毕后,重新登陆后起效(不需要重启服务)
永久设置阈值:
/etc/my.cnf中追加配置:
vim /etc/my.cnf
[mysqld]
long_query_time=3;

### 如何编写SQL语句以避免索引失效 为了确保SQL查询能够高效运行并充分利用已有的索引结构,以下是关于如何避免索引失效的一些最佳实践: #### 1. 遵守最左前缀原则 当使用复合索引时,必须按照索引定义的顺序来匹配字段。如果跳过了某个中间字段,则后续字段无法利用索引。例如,对于一个 `(a, b, c)` 的复合索引,只有 `WHERE a=...` 或者 `WHERE a=... AND b=...` 能够有效利用索引[^1]。 #### 2. 避免在索引列上进行函数操作 任何对索引列的操作都会导致索引失效。比如,`SELECT * FROM table WHERE YEAR(date_column) = 2023;` 这样的查询会使得 MySQL 无法直接使用 `date_column` 上的索引。推荐改写为: ```sql SELECT * FROM table WHERE date_column >= '2023-01-01' AND date_column < '2024-01-01'; ``` #### 3. 使用覆盖索引 覆盖索引是指查询所需的数据完全可以通过索引来获取而无需回表读取数据行的情况。因此,在设计 SQL 查询时,尽量只选择那些被索引覆盖的列,而不是简单地使用 `SELECT *`[^3]。例如: ```sql -- 不好的做法 SELECT * FROM users WHERE status = 'active'; -- 更优的做法 SELECT id, name FROM users USE INDEX (status_index) WHERE status = 'active'; ``` #### 4. 防止隐式的类型转换 当查询条件中的数据类型与数据库中存储的实际数据类型不一致时,可能会触发隐式类型转换,从而破坏索引的有效性。例如,字符串类型的字段不应省略单引号;整数比较也不应该加多余的引号[^1]。下面是一个例子: ```sql -- 错误示例 SELECT * FROM products WHERE price = '99'; -- 如果price是数值型,这里会导致索引失效 -- 正确示例 SELECT * FROM products WHERE price = 99; ``` #### 5. 合理处理LIKE模式匹配 带有通配符 `%` 和 `_` 的模糊查找会影响索引效率。特别是当 `%` 放置于开头位置时(如 `'%abc%'`),几乎总是会造成全表扫描。为了避免这种情况发生,可以考虑全文检索或者调整业务逻辑使 `%` 只出现在右侧[^1]。如下所示: ```sql -- 效率较低的方式 SELECT * FROM articles WHERE title LIKE '%keyword%'; -- 较高效的替代方案 SELECT * FROM articles WHERE title LIKE 'keyword%'; ``` #### 6. 对NULL值和OR运算保持警惕 涉及 NULL 判断以及 OR 条件组合的情况下容易引起复杂度上升甚至放弃走索引路径。一种解决办法就是拆分多个独立子查询再 UNION ALL 结合起来代替单一的大规模过滤表达式。举个实例说明这一点: ```sql -- 存疑形式 SELECT * FROM orders WHERE customer_id IS NULL OR order_status='shipped'; -- 推荐重构版 (SELECT * FROM orders WHERE customer_id IS NULL) UNION ALL (SELECT * FROM orders WHERE order_status='shipped'); ``` #### 7. 强制指定使用的索引 尽管通常情况下让 DBMS 自动决定最优执行计划即可满足需求,但在某些特殊场景下可能需要人为干预指导其行为。这时就可以借助 `FORCE INDEX`, `USE INDEX` 关键字显式声明希望优先尝试哪些候选索引[^2]。示范代码片段如下: ```sql SELECT * FROM employees FORCE INDEX(last_name_idx) WHERE last_name='Smith'; ``` --- ### 总结 遵循以上提到的各项准则可以帮助开发者构建更加精炼有效的 SQL 查询脚本,进而显著改善应用程序的整体响应速度和服务质量。当然除了理论学习之外还需要不断积累实战经验才能真正做到游刃有余。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值