SQL分析主要有两个切入点
EXPLAIN执行计划分析
数据库慢SQL查询
EXPLAIN执行计划分析
id
id是查询序列编号,每张表都是单独访问的,一个SELECT就会有一个序号。
在连接查询中,先查询的叫做驱动表,后查询的叫做被驱动表。在实际应用场景中,尽可能的把小表放在前面查询,大表放后。(小表驱动大表的思想)
总结:id值不同时,表的查询顺序是先大后小。id值相同时,表的查询顺序是从上往下执行。
select_type
select_type表示查询类型,它的常用取值类型如下:
- SIMPLE:简单查询,表示此查询不包含 UNION 查询或子查询
- PRIMARY:表示SQL语句中的主查询,也就是最外层的查询
- DERIVED:衍生查询,表示得到最终结果之前会用到临时表
- UNION:表示此查询是 UNION 的第二或随后的查询
- UNION RESULT:UNION 的结果,table列显示是哪些序列的UNION结果
- DEPENDENT UNION, UNION 中的第二个或后面的查询语句, 取决于外面的查询
- SUBQUERY:子查询中的所有内查询,即SELECT或者WHERE中包含的子查询
- DEPENDENT SUBQUERY: 子查询中的第一个 SELECT, 取决于外面的查询. 即子查询依赖于外层查询的结果
DERIVED 和 UNION 的执行顺序:先执行UNION右边的查询,再执行UNION左边的查询,此时左边表的查询类型是DERIVED。
type连接类型
type字段比较重要,它提供了判断查询是否高效的重要依据,可以判断查询是全表扫描还是索引扫描等。常见的类型有 const、system、eq_ref、ref、range、index、all
const:针对主键或者唯一索引的等值查询,最多返回一条数据。const查询速度非常快,因为它仅需读取一次即可。
system:查询表中只有一条数据时,是特殊的const类型。对于MyISAM 和 Memory的表,只查询到一条数据,也是system。
eq_ref:在多表连接查询中,被驱动表通过唯一索引(UNIQUE KEY 或者 PRIMARY KEY)进行访问时,被驱动表的访问方式就是eq_ref。
例如:
-- ALTER TABLE teacher_contact DROP PRIMARY KEY;
ALTER TABLE teacher_contact ADD PRIMARY KEY(tcid); --增加主键索引
explain select t.tcid from teacher t,teacher_contact tc where t.tcid = tc.tcid;
执行计划如下:
ref:查询用到了非唯一索引,或者关联操作只使用了索引的最左前缀,则连接类型是ref。
range:范围查询。如果where后面是 between and 或 <或 > 或 >= 或 <=或in这些,type类型就为range。 MySQL中in的参数个数不受限制,但是长度会受到限制,一般为64M。in通常会走索引,但是in的个数较多时,就不会再走索引,而是进行全表扫描。
index:Full Index Scan,全索引扫描。
index 和 all 的区别是,index只扫描索引树即可,所以它比all的查询效率要高。
例如:EXPLAIN SELECT tid FROM teacher; -- tid为主键
all:Full Table Scan,全表扫描。
null:不用访问表或者索引就能得到结果,例如:EXPLAIN select 1 from table1;
possible_key、key
possible_key表示可能用到的索引,key表示实际用到的索引。如果这一列为空,表示没有用到索引。 possible_key可以有一个或者多个,当然,可能用到索引不代表一定用到索 引。
key_len
索引的长度(使用的字节数)。跟索引字段的类型、长度有关。
例如:varchar(255),在utf8mb4编码中,一个字符占了4个字节,所以是255 * 4 = 1020。另外使用可变长度varchar,需要额外增加2个字节,允许为null需要额外增加1个字节,所以一共是1023个字节长度。
因此,key_len越长,表示使用的索引范围越广。
rows
表示扫描多少行才能返回请求的数据,是一个预估值,一般来说行数越少越好。
filtered
filtered表示返回结果的行数占需读取行数的百分比,它只对index 和 all 的扫描有效。如果比例很低,说明存储引擎层返回的数据需要经过大量过滤,这个会消耗性能的,需要关注。
ref
表示使用哪个 列 或者 常数 或者 索引
例如:表示用到了两个常量进行数据的筛选。
Extra
EXPLAIN中的很多额外信息都会在改字段中进行显示。
- Using filesort: 表示 MySQL 需额外的排序操作,不能通过索引顺序达到排序效果。一般如果有 Using filesort,,都建议优化去掉, 因为这样的查询 CPU 资源消耗大。
- Using index :覆盖索引扫描,表示查询在索引树中就可查找所需数据,,不用扫描表数据文件,往往说明性能不错。
- Using temporary:查询有使用临时表,一般出现于排,分组和多表 join 的情况,查询效率不高,建议优化。
EXPLAIN执行计划官方地址:https://dev.mysql.com/doc/refman/8.0/en/explain-output.html
数据库慢查询日志
慢查询的监控:
- Druid包提供了慢查询的监控
- Mysql提供了慢查询日志
MySQL慢查询开启
show variables like 'slow_query%' ;
show_query_log: 默认是关闭的(开启它需要代价,因为需要写文件)
show_query_log_file: 慢日志存放的目录
慢查询判定配置
show variables like '%long_query%' ;
默认是10s,也就是记录查询时间大于10s的sql,这三个参数可以在 my.cnf 文件中修改。
slow_query_log = ON
long_query_time=2
slow_query_log_file =/var/lib/mysql/localhost-slow.log
慢查询日志分析
例如:查询user_innodb这个表(300W数据),在没有任何索引的情况下查询(SELECT * FROM user_innodb where phone='180'; ),会耗时较长,于是我们在/var/lib/mysql/localhost-show.log 目录下的文件,内容如下:
# Time: 2022-03-08T19:15:54.086977Z
# User@Host: root[root] @ [192.168.8.16] Id: 12
# Query_time: 2.800553 Lock_time: 0.000517 Rows_sent: 3000001
Rows_examined: 3000001
SET timestamp=1646766951;
select * from user_innodb;
如果文件内容较大,可以通过日志监控的方式,或者是用mysql提供的mysqldumpslow工具来分析。mysqldumpshow是MySQL提供的分析工具,在/usr/bin/mysqldumpshow目录下。https://dev.mysql.com/doc/refman/8.0/en/mysqldumpslow.html
mysqldumpshow使用:
例如查询用时最多的10条慢SQL:mysqldumpslow -s t -t 10 -g 'select' /var/lib/mysql/localhost-
slow .log。 其中 -s 表示如何排序, t 表示查询时间或者平均查询时间 ; -t 查询条数 ; - g 相当于 grep ,从目标文件中匹配的关键字。
结果如下:
参数说明:
- Count代表这个SQL执行了多少次;
- Time代表执行的时间,括号里面是累计时间;
- Lock表示锁定的时间,括号是累计;
- Rows表示返回的记录数,括号是累计。