1.找到执行效率低的sql语句。有两个手段,第一是通过慢查询日志。我们在mysql打开慢查询日志,这样执行慢的sql语句就会被记录到我们的日志文件中。第二是通过show processlist来实时观察mysql正在运行的sql语句。
分析sql语句运行效率低的原因.
2.mysql中提供了执行计划命令可以帮助我们观察一个sql语句执行时候经历了什么样的操作。比如用没有索引,回表了没有,索引下推了没有,我们都可以通过这个执行计划观察到。查看执行计划的命令是explain sql命令就可以了。通过explain的查询结果我们可以分析该sql语句是否使用到了索引,是否使用索引下推这些功能,然后针对性的进行性能的优化。
3.优化sql。最左匹配原则,避免使用like,like有可能会导致索引失效。避免使用or。尽量使用覆盖索引。模糊查询,查询类型不匹配,字段上的函数运算,查询is null或者is not null,在字段上面做运算,都要尽量避免。
3.1 定位低效率执行SQL
可以通过以下两种方式定位执行效率较低的 SQL 语句。同时sql执行顺序也会有一定的影响
SQL语句的执行顺序可以分为以下几个步骤:
-
FROM子句:首先,SQL引擎会从FROM子句中指定的表中获取数据。如果有多个表,会进行表的连接操作。
-
WHERE子句:接下来,SQL引擎会根据WHERE子句中的条件对数据进行筛选,只选择满足条件的数据。
-
GROUP BY子句:如果有GROUP BY子句,SQL引擎会根据指定的列对数据进行分组。
-
HAVING子句:在GROUP BY子句之后,如果有HAVING子句,SQL引擎会根据指定的条件对分组后的数据进行筛选。
-
SELECT子句:在前面的步骤完成后,SQL引擎会根据SELECT子句中指定的列,从筛选后的数据中获取需要的数据。
-
ORDER BY子句:如果有ORDER BY子句,SQL引擎会根据指定的列对数据进行排序。
-
LIMIT子句:最后,如果有LIMIT子句,SQL引擎会根据指定的数量限制返回的结果集的行数。
需要注意的是,这只是一般情况下的执行顺序,实际执行顺序可能会根据具体的数据库优化器和执行计划进行调整。此外,子查询、连接操作、索引等也会影响SQL语句的执行顺序。
SQL执行顺序的优化可以提高SQL的执行效率。通过优化SQL的执行顺序,可以减少不必要的数据读取和计算,从而提高查询的性能。
慢查询日志 : 通过慢查询日志定位那些执行效率较低的 SQL 语句,mysqld 写一个包含所有执行时间超过 long_query_time 秒的 SQL 语句的日志文件。
show variables like '%slow_query_log%';//是否开启慢查询 set global slow_query_log=1; show variables like '%long_query_time%';//慢sql的阈值
show processlist : 慢查询日志在查询结束以后才纪录,所以在应用反映执行效率出现问题的时候查询慢查询日志并不能定位问题,可以使用show processlist命令查看当前MySQL在进行的线程,包括线程的状态、是否锁表等,可以实时地查看 SQL 的执行情况,同时对一些锁表操作进行优化。
show processlist;
列名 | 说明 |
---|---|
id | 用户登录mysql时,系统分配的"connection_id",可以使用函数connection_id() |
user | 显示当前用户。 |
host | 显示这个语句是从哪个ip的哪个端口上发的,可以用来跟踪出现问题语句的用户 |
db | 显示这个进程目前连接的是哪个数据库 |
command | 当前连接的执行的命令,一般取值为休眠(sleep),查询(query), 连接(connect)等 |
time | 显示这个状态持续的时间,单位是秒 |
state | 显示使用当前连接的sql语句的状态,很重要的列。state描述的是语句执行中的某一个状态。一个sql语句,以查询为例,可能需要经过copying to tmp table、sorting result、sending data等状态才可以完成 |
info | 显示这个sql语句,是判断问题语句的一个重要依据 |
3.2 explain分析执行计划
通过以上步骤查询到效率低的 SQL 语句后,可以通过 EXPLAIN命令获取 MySQL如何执行 SELECT 语句的信息,包括在 SELECT 语句执行过程中表如何连接和连接的顺序。
-
准备数据:
create table tb_item( item_id int NOT NULL AUTO_INCREMENT, item_name varchar(100) not null, item_price int not null, primary key(item_id) )engine=InnoDB default charset=utf8; delimiter $ create procedure pro_item_insert() begin declare num int(11) default 1000000; declare random_num VARCHAR(60); ins:loop if num<=0 then leave ins; end if; SELECT round(round(rand(),10)*1000000000) into random_num; insert into tb_item values(null,CONCAT('小米mix',random_num),random_num); set num = num -1; end loop ins; end$ delimiter ; call pro_item_insert();
-
查询SQL语句的执行计划 :
explain select * from tb_item where item_id = 1;
针对上面的查询的每个字段进行解释说明:
字段 | 含义 |
---|---|
id | select查询的序列号,是一组数字,表示的是查询中执行select子句或者是操作表的顺序。 |
select_type | 表示 SELECT 的类型,常见的取值有 SIMPLE(简单表,即不使用表连接或者子查询)、PRIMARY(主查询,即外层的查询)、UNION(UNION 中的第二个或者后面的查询语句)、SUBQUERY(子查询中的第一个SELECT)等 |
table | 输出结果集的表 |
type | 表示表的连接类型,性能由好到差的连接类型为( system ---> const -----> eq_ref ------> ref-------> ref_or_null----> index_merge ---> index_subquery -----> range -----> index ------>all ) |
possible_keys | 表示查询时,可能使用的索引 |
key | 表示实际使用的索引 |
key_len | 索引字段的长度 |
ref | 列与索引的比较 |
rows | 扫描行的数量 |
Extra | 执行情况的说明和描述 |
1. explain 之 type
type 显示的是访问类型,是较为重要的一个指标,可取值为:
TYPE的值 | 含义 |
---|---|
NULL | MySQL不访问任何表,索引,直接返回结果:explain select 'Hello'; |
system | 表只有一行记录(等于系统表),这是const类型的特例,一般不会出现 |
const | 表示通过索引一次就找到了,const 用于比较primary key 或者 unique 索引。因为只匹配一行数据,所以很快。如将主键置于where列表中,MySQL 就能将该查询转换为一个常量。const将"主键" 或 "唯一" 索引的所有部分与常量值进行比较 |
eq_ref | 类似ref,区别在于使用的是唯一索引,使用主键的关联查询,关联查询出的记录只有一条。常见于主键或唯一索引扫描 |
ref | 非唯一性索引扫描,返回匹配某个单独值的所有行。本质上也是一种索引访问,返回所有匹配某个单独值的所有行(多个) |
range | 只检索给定返回的行,使用一个索引来选择行。 where 之后出现 between , < , > , in 等操作。 |
index | index 与 ALL的区别为 index 类型只是遍历了索引树, 通常比ALL 快, ALL 是遍历数据文件。 |
all | 将遍历全表以找到匹配的行 |
结果值从最好到最坏依次是:
NULL > system > const > eq_ref > ref > fulltext > ref_or_null > index_merge >unique_subquery > index_subquery > range > index > ALL --常见的 system > const > eq_ref > ref > range > index > ALL
一般来说, 我们需要保证查询至少达到 range 级别, 最好达到ref 。
2. explain 之 extra
其他的额外的执行计划信息,在该列展示 。
EXTRA的值 | 含义 |
---|---|
using filesort | 说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取,称为“文件排序”, 效率低。 |
using temporary | 使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于 order by 和 group by; 效率低 |
using index | 表示相应的select操作使用了覆盖索引, 避免访问表的数据行, 效率不错。 |
using where | 需要回表查询 |
3.3 常见的优化手段
-
不要使用select *,要使用select字段名
-
小表驱动大表。
-
使用索引,注意索引的一些问题。
-
全值匹配 ,对索引中所有列都指定具体值。 最左前缀法则 。不要在索引列上进行运算操作, 否则索引将失效。 字符串不加单引号,造成索引失效。 尽量使用覆盖索引 。 用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。 以%开头的Like模糊查询,索引失效. 如果MySQL评估使用索引比全表更慢,则不使用索引。 is NULL , is NOT NULL 有时索引失效. 尽量使用复合索引,而少使用单列索引 。 尽量减少额外的排序,通过索引直接返回有序数据。where 条件和Order by 使用相同的索引,并且Order By 的顺序和索引顺序相同, 并且Order by 的字段都是升序,或者都是降序。否则肯定需要额外的操作,这样就会出现FileSort。