当前位置: 首页 > news >正文

【MySQL】SQL优化

目录

一.EXPLAIN执行计划

1.用法

2.explain字段

二.WHERE子句优化

三.范围优化

1.单部索引范围访问

2.多部索引范围访问

四.索引合并与索引下推

1.索引合并

2.索引下推

六.IS NULL优化

七.ORDER BY优化

1.可以使用索引来解析ORDER BY

2.不使用索引来处理ORDER BY,但可以使用索引作为where条件

八.聚合查询优化

1.GROUP BY优化

2.DISTINCT优化

九.索引失效

十.索引使用原则

一.EXPLAIN执行计划

explain可以分析SQL语句的执行情况,以便我们优化SQL语句。注意,explain并不是真正的执行SQL语句,只是分析SQL语句。

1.用法

直接在explain后加上要分析的SQL语句

explain SQL语句

2.explain字段

下面简洁说明一下explain字段:

列名说明
idselect标识符,查询语句中select的序号。如果查询语句中包含子查询或合并查询,则每个查询的编号依次递增
select_typeselect类型
table查询的表
partitions查询的分区,对于非分区表,值为NULL
typeJOIN类型
possible_keys可能选择的索引
key实际选择的索引
key_len索引的字节长度,如果key为NULL,那么长度也为NULL
ref
在查询过程中哪些列或常量与key列中指定的索引进行比较
rows估算要检查的行数,数据越小越好
filtered按条件筛选行的百分比,数据越大越好
Extra附加信息

下面对部分字段进行详细介绍:

1)select_type 列

select类型:

select_type值说明
SIMPLE简单查询,没有子查询或合并查询
PRIMARY外层查询
UNIONUNION中的第二个及之后的查询语句
UNION RESULTUNION的结果
SUBQUERY子查询中
INSERTinsert语句
UPDATEupdate语句
DELETEdelete语句

2)table 列

查询时数据行所在表的名称

<union M,N>:表示合并查询用的表,M和N是id列的值

<subquery N>:表示子查询用的表,N是id列的值

3)type 列

展示表是如何连接的,性能排序如下:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge >
unique_subquery > index_subquery > range > index > ALL
类型说明
system
表中只有一行数据,不用任何扫描,性能极高,是const类型的特殊情况,一般是在MyISAM存储引擎中
const

当查询中的条件通过主键索引或唯⼀索引与常量进行比较时,结果最多有⼀个匹配的行,查询性能极高,且只会返回一行数据

eq_ref唯一非空索引列,应用于多表连接的场景,表关联条件是主键索引或唯⼀非空索引时使用等号 ( = ) 进行索引列的比较,每行只匹配⼀条记录
ref
非空但不唯一,SQL语句中使用了普通索引,返回的结果可能是多行组成的结果集
fulltext
使用全文索引
ref_or_null不唯一,索引列必须是可以为空的列
index_merge
在查询中使用了多个索引,OR 两边必须是单独索引,最终通过不同索引检索数据,然后对结果集进行合并,Key_len显示最长的索引长度
unique_subquery

子查询中返回的是外层表的主键索引或唯一索引

index_subquery子查询中返回的是外层表的普通索引
range
使⽤索引列进行范围查询,当使用<>、>、>=、<、<=、is NULL、<=>、BETWEEN、LIKE或IN()操作符,索引列与常量进行比较
index
扫描整个索引树而不扫描整个表,比如只使用索引排序而不使用条件查询
ALL
最差的情况,表示MySQL必须对全表进行逐行扫描才可以以找到匹配行,遇到这种情况通常建议在查询的列上加索引来避免全表扫描

我们在实际优化SQL的时候,要尽量避免最后三个,最后三个效率低。

4)Extra 列

类型说明
Using temporary
当使用非索引列进行分组时,会用临时表进行排序,优化时可以考虑为分组的列加索引
Using filesort
当使用非索引列进行排序时会用到为文件内排序,优化时可以考虑为排序的列加索引
Using where
当使用非索引列进行检索数据,且进行了全表扫描
Using index
发生索引覆盖时显示using index,表示这是⼀个高效查询

什么是索引覆盖

简单来说就是:要使用的索引中包含要查询列,这时直接返回索引中列的值,而不用回表查询

当创建普通索引时,生成对应的索引树。普通索引的索引树的叶子结点保存的是索引列的值和主键值。当查询的结果列在索引树中可以找到,那么就直接从索引树中返回结果,这就是索引覆盖。

什么是回表查询

当使用索引检索数据时,查询的列不只包含索引列,这时需要通过索引中记录的主键值到主表中进行查询,这个现象叫做回表查询。

二.WHERE子句优化

不仅适用于select,update、delete也可以。

1.删除不必要的括号

-- 优化前
((a AND b) AND c OR (((a AND b) AND (c AND d))))
-- 优化后
(a AND b AND c) OR (a AND b AND c AND d)

2.常量合并

-- 优化前
(a<b AND b=c) AND a=5
-- 优化后
b>5 AND b=c AND a=5

3.常量条件去除

-- 优化前
(b>=5 AND b=5) OR (b=6 AND 5=5) OR (b=7 AND 5=6)
-- 优化后
b=5 OR b=6

4.删除无效或超出范围的值

-- 表结构如下,有⼀个TINYINT 类型的列 (1byte)
CREATE TABLE t (c TINYINT UNSIGNED NOT NULL);
-- 优化前
SELECT * FROM t WHERE c < 256;
-- 优化后
SELECT * FROM t WHERE 1;

5.优先查询常量表

什么是常量表?

MySQL中常量表分为两种类型:

1)System表:只包含一行数据的表。

2)Const表:通过where语句中的限制条件筛选后只包含一行数据的表。这个条件通常是某列等于要给常量值,该列上通常有索引,可能是主键或唯一键。MySQL可以通过索引快速定位该行。

-- 单表中⽤主键查询
SELECT * FROM t WHERE primary_key=1;
-- 多表中⽤主键查询
SELECT * FROM t1,t2
WHERE t1.primary_key=1 AND t2.primary_key=t1.id;

注意:在表连接查询中,读取任何其他表之前先读取所有常量表。

6.高效查询示例

-- 统计所有⾏数
SELECT COUNT(*) FROM tbl_name;-- 聚合函数中使⽤索引列
SELECT MIN(key_part1),MAX(key_part1) FROM tbl_name;-- 复合索引时,where条件使⽤索引列1,聚合函数中使⽤其他索引列
SELECT MAX(key_part2) FROM tbl_name
WHERE key_part1=constant;-- 排序时使⽤索引列、复合索引时,按索引列的顺序排序
SELECT ... FROM tbl_name
ORDER BY key_part1,key_part2,... LIMIT 10;-- 排序时使⽤索引列,复合索引时,按索引列的顺序排序,可以为不同的列指定排序规则
SELECT ... FROM tbl_name
ORDER BY key_part1 DESC, key_part2 DESC, ... LIMIT 10;

三.范围优化

在范围查询的列上建立索引可以有效的提高查询效率。注意,这里的范围不是特指一个区间。

1.单部索引范围访问

范围条件定义如下:

1)对于BTREE和HASH索引使用=、<=>、IN()操作符时,索引部分与常量值的比较是一个范围条件;

2)对于BTREE索引当使用>、<、>=、<=、BETWEEN、<>操作符时,索引部分与常量值的比较是一个范围条件;

3)LIKE的参数是一个不以通配符开头的常量字符串也是一个范围条件;

4)对于所有索引类型,多个范围条件用OR或AND组合形成一个范围条件。

注:常量值是查询字符串中的常量,表连接中const表或system表中的列,或由上述类型组成的表达式。

2.多部索引范围访问

多部索引范围访问是单部索引范围条件的扩展,使用复合索引中的多个索引列进行范围查询。

四.索引合并与索引下推

1.索引合并

多条件的查询语句中where查询通常会包含多个条件判断,以AND或OR操作进行连接。在对⼀个表进行查询时最多只能利用该表上的⼀个索引,其他条件需要在回表查询时进行判断(不考虑覆盖索引的情况)。当回表的记录数很多时,需要进行大量的随机IO,这可能导致查询性能下降。

索引合并是通过对⼀个表同时使用多个索引进行条件扫描,并将满足条件的多个主键集合取交集或 并集后再进行回表,可以提升查询效率。 索引合并可以检索多个范围扫描的数据行,并将结果合并为⼀个。索引合并仅合并单个表中的索引扫描,而不能跨表扫描。合并的结果是多个索引扫描的并集、交集或交集的并集。

在explain输出中索引合并在type列中显示为index_merge。具体算法有以下三种:

1)交叉访问算法

适用于将⼀个WHERE子句中的条件转换为多个不同索引的范围条件并结合AND⼀起使用。

2)联合访问算法

适用于将⼀个WHERE子句中的条件转换为多个不同索引的范围条件并结合OR⼀起使用。

3)排序联合算法

联合访问算法只实现了对二级(普通)索引进行等值查询,但更多的时候需要对⼆级索引进行范围查找,排序联合算法支持了二级索引的范围查找。

2.索引下推

当使用索引进行范围或不以%开头的模糊查询时,先过滤出⼀个范围,如果没有索引下推则需要 根据主键回表后再判断其他的where条件。如果使用复合索引且其他的条件可以用复合索引中的列判断,则在存储引擎层面就进行过滤,而不用回表查询后再过滤,最终会减少回表的次数,提升效率。

六.IS NULL优化

MySQL可以对列名 IS NULL与列名 = 常量值相同的优化。

建议在建表的时候为非空的列指定NOT NULL约束。

如果where子句对NOT NULL的列添加列名 IS NULL条件,那么表达式将被优化掉:

-- 主键不可能为空,执⾏计划中显⽰ Impossible WHERE 表⽰当前条件永远不可能为真
select * from t1 where id is null;
-- ⾮空列不可能为空,执⾏计划中显⽰ Impossible WHERE
select * from t1 where a is null;
-- 条件组合中存在is null 也会被优化掉
select * from t1 where a = 'aaa' or a is null;

七.ORDER BY优化

MySQL可以使用索引进行ORDER BY排序,不能使用索引的时候,会通过filesort来处理结果的排序操作,所以要尽量使用索引。

1.可以使用索引来解析ORDER BY

联合索引是(key_part1, key_part2)

1)排序时直接使用索引列

SELECT * FROM t1 ORDER BY key_part1, key_part2;

2)key_part1是常数,通过索引访问的所有行都按照key_part2排序

SELECT * FROM t1 WHERE key_part1 = constant ORDER BY key_part2;

3)ORDER BY对索引列进行升序或降序排序

SELECT * FROM t1 ORDER BY key_part1 ASC, key_part2 ASC;

4)key_part1与常量比较,并使用key_part1排序

SELECT * FROM t1 WHERE key_part1 > constant ORDER BY key_part1 ASC;

2.不使用索引来处理ORDER BY,但可以使用索引作为where条件

1)对不同的索引使用ORDER BY

-- key1, key2是两个不同的普通索引
SELECT * FROM t1 ORDER BY key1, key2;

2)不符合最左前缀匹配原则

SELECT * FROM t1 ORDER BY key1_part1, key1_part3;

3)WHERE中的索引与ORDER BY的索引不一致

SELECT * FROM t1 WHERE key2=constant ORDER BY key1;

4)ORDER BY中对索引进行转换和运算

SELECT * FROM t1 ORDER BY ABS(key);
SELECT * FROM t1 ORDER BY -key;

八.聚合查询优化

1.GROUP BY优化

在执行分组查询时,会先把符合where条件的结果保存在一个新创建的临时表中,临时表中每个分组字段的所有行都是连续的,然后再分离每个组并用聚合函数。由于索引本身就是连续的,因此可以使用使用来避免创建临时表。

以下分组查询可以用到索引:

联合索引是(c1,c2)

1)分组时使用索引列并遵循最左前缀匹配原则

SELECT c1, c2 FROM t2 GROUP BY c1, c2;

2)对于同一个索引使用MAX()或MIN()

SELECT c1, MIN(c2), MAX(c2) FROM t2 GROUP BY c1;

3)如果使用其他聚合函数,必须加DISTINCT

-- 如果使⽤其他聚合函数,必须加DISTINCT
SELECT COUNT(DISTINCT c1), SUM(DISTINCT c1) FROM t2;
-- 对两个列进⾏COUNT(),必须加DISTINCT,否则会报语法错误
SELECT COUNT(DISTINCT c1, c2), COUNT(DISTINCT c2,c1) FROM t2;

2.DISTINCT优化

distinct子句查询可以理解成分组查询的特殊情况,参考分组查询优化。

九.索引失效

1.违反最左前缀匹配原则

举个例子:

联合索引(a, b, c)where a=1 and b=2  # 走联合索引where b=2 and a=1  # 走联合索引, 不会因为where后的字段顺序就失效where b=2 and c=1  # 不会走联合索引, 因为a字段在索引最左侧, where中没有awhere c=1 and a=2  # 会走联合索引关于a的部分

2.where中有or,or的两边的列有不是索引列

举个例子:

select * from index_demo where id = '1020000' or age = 20
# id是索引列,但是age不是

3.复合索引中第一个使用范围查询的条件,或不以%开头的模糊查询之后的列不使用索引

举个例子:

select * from index_demo where age < 18 and class_id > 4

4.模糊查询以%开头

举个例子:

select * from index_demo where name like '%user_1022000'

5.隐式转换,比如字符串没有加引号

select * from index_demo where sn = 1020000
# sn是varchar类型的

6.where子句中有表达式或函数

# 表达式
select * from index_demo where id + 1 = 1000020 # 不走索引
select * from index_demo where id = 1000020 + 1 # 走索引
# 函数
select * from index_demo where length(name) = 11 # 不走索引

7.使用<>,not in

select * from index_demo where name <> 'user1020000'

8.MySQL判断全表扫描比索引快时不使用索引,常发生在数据量极少的表中,或是过滤条件效果不佳时

十.索引使用原则

1.每张表必须有主键,推荐BIGINT类型自增

2.DISTINCT,ORDER BY,GROUP BY,JOIN条件,WHERE条件的列加索引;

3.对频繁进行数据操作的表,不要建立太多索引,因为维护索引也需要很大的成本;

4.使用复合索引时遵守最左前缀原则

5.多表JOIN有确定条件时WHERE class_id = 1,可以分成多个单表查询,然后在程序中合并;

6.避免在重复值太多(区分度低)的列上建立索引,比如大部分都是相同的值,查了索引之后还会回表,那么还不如全表扫描;

7.使用指定的索引use index(index_name),force index(index_name);

8.创建索引时可以指定ASC DESC,create index index_name(col desc, col1 asc);

9.创建⼀个复合索引(a, b, b),相当于同时创建了(a),(a,b),(a,b,c);

10.创建索引之前,确保当前数据库实例没有未提交的大事务,防止数据锁死;

11.对不常使用的索引进行清理,删除无用的索引,避免对执行计划造成负面影响。

http://www.dtcms.com/a/320413.html

相关文章:

  • Linux Shell为文件添加BOM并自动转换为unix格式
  • C++之队列浅析
  • 每日算法刷题Day58:8.7:leetcode 单调栈5道题,用时2h
  • 零基础-动手学深度学习-9.3. 深度循环神经网络
  • Langchain入门:对话式RAG
  • Tool Learning的基本概念及应用
  • 数据结构——栈、队列
  • python题目练习 无重叠区间
  • Linux学习-数据结构(二叉树)
  • 嵌入式开发学习———Linux环境下IO进程线程学习(六)
  • 了解大型语言模型:力量与潜力
  • SpringBoot学习日记 Day5:解锁企业级开发核心技能
  • PCIe Base Specification解析(九)
  • 多线程的使用
  • 2025 最新 ECharts 下载、安装与配置教程
  • Linux 中断系统全览解析:从硬件到软件的全路线理解
  • Oracle 19C In-Memory 列存储技术测试
  • Qwen系列模型
  • [链表]两两交换链表中的节点
  • 【感知机】感知机(perceptron)学习算法的对偶形式
  • aurora rx没有ready信号
  • 哈希表——指针数组与单向链表的结合
  • linux顽固进程查看并清理
  • Java包装类详解与应用指南
  • SupChains技术团队:需求预测中减少使用分层次预测(五)
  • 目标检测数据集 - 眼睛瞳孔检测数据集下载「包含COCO、YOLO两种格式」
  • 菜鸟笔记007 [...c(e), ...d(i)]数组的新用法
  • (数据结构)顺序表实现-增删查改
  • java中override和overload的区别
  • 敏捷总结-上