【深入浅出mysql(第二版)18章学习笔记】
了解SQL的执行频率之后,再继续了解如何分析SQL语句。分析SQL语句可以通过EXPLAIN、show profile、trace对SQL进行综合分析,其中EXPLAIN比较常用。
1 通过 EXPLAIN 分析低效SQL的执行计划
Explain可以定位SQL的执行问题,例如:
EXPLAIN SELECT sum(amount) from customer a, payment b WHERE 1=1 and a.customer_id = b.customer_id and email = 'JANE.BENNETT@sakilacutomer.org';
输出结果:
各个列名的含义:
select_type : 表示select的类型,常见取值如下:
SIMPLE :简单表,即不使用表连接或子查询;
PRIMARY: 主查询,即外层查询;
UNION : UNION 中的第二个或者后面的查询语句;
SUBQUERY : 子查询中的第一个select。
table:输出结果集的表;
type : 表示Mysql在表中找到找到所需行的方式,或者叫访问类型。常见类型如下:
ALL | index | range | ref | eq_ref | const/system | NULL
从左到右,性能由最差到最好。
possible_keys : 表示查询时可能使用到的索引;
key : 表示实际使用的索引;
key_len : 使用到索引字段的长度;
ref :
rows: 扫描行的数量;
Extra: 执行情况的说明和描述,包含不适合在其他列中显示但是对执行计划非常重要的额外信息。
详解 type 列:
(1)type=ALL
全表扫描,mysql遍历全表来找到匹配的行
EXPLAIN SELECT * FROM film WHERE rating > 9 ;
结果如图:
(2)type=index
索引全扫描,mysql遍历整个索引来查询匹配的行
EXPLAIN SELECT title FROM film;
结果如图:
(3)type=range
索引范围扫描,常见于** <、 <=、 >、 >= 、between**等操作符。
EXPLAIN SELECT * FROM payment WHERE customer_id > 300 AND customer_id <=350;
结果如图:
(4)type=ref
使用非唯一索引扫描或唯一索引的前缀扫描,返回匹配某个单独值的记录行
EXPLAIN SELECT * FROM payment WHERE customer_id =350;
结果如图:
索引 idx_fk_customer_id 是非唯一索引。
(5)type=eq_ref
与 ref 类似 ,区别在于使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配。即在多表连接中使用 primary key或者 unique index 作为关联条件。
EXPLAIN SELECT * FROM film a, film_text b WHERE a.film_id = b.film_id;
结果如图:
(6)type=const/system
单表中最多有一个匹配行,查询起来非常迅速,所以这个匹配行中的其他列的值可以被优化器在当前查询中当作常量来处理。例如,根据主键 primary key 或者唯一索引 unique index 进行的查询。
ALTER TABLE customer add UNIQUE INDEX uk_email (email);
EXPLAIN SELECT * FROM (SELECT * FROM customer WHERE email = 'AARON.SELBY@sakilacustomer.org') a;
结果如图:
通过唯一索引 uk_email 访问的时候,类型 type=const;而仅有一条记录的表中检索时,类型 type = system。
(7)type=NULL
mysql 不用访问表或者索引,直接就能得到结果。
EXPLAIN SELECT 1 FROM DUAL where 1;
结果如图:
2)通过 show profile 分析SQL
【主要查看SQL语句执行的时候,时间耗费在什么地方】
首先查询当前mysql是否支持 profile (profile 可以帮助了解SQL执行的过程)
SELECT @@have_profiling;
如果profiling是关闭的,可以通过set语句在Session级别开启profiling:
SELECT @@profiling;
SET profiling=1;
关于profile的作用:
第一步:执行一个count(*)查询
SELECT COUNT(*) FROM payment;
第二步:查看当前SQL的query id
show PROFILES;
第三步:通过 show profile for query 语句查看执行过程中线程的每个状态和消耗的时间
show PROFILE for QUERY 167;
注:
-
[ ]Sending data 状态表示mysql线程开始访问数据并把结果返回给客户端,而不仅仅是返回结果给客户端。由于在Sending data状态下,mysql线程需要做大量的磁盘读取操作,所以是整个查询过程中耗时最长的状态。
-
[ ]show profile for query 语句也支持选择all、 CPU、block io 、 context switch、page faults 等明细类型来查看mysql在使用什么资源上耗费了过高的时间。例如,查看cpu的耗费时间:
3) 通过trace分析优化器如何选择执行计划
首先打开trace ,设置格式为JSON,设置trace最大能够使用的内存大小。
SET OPTIMIZER_TRACE="enabled=on",end_markers_in_json=on;
SET optimizer_trace_max_mem_size=1000000;
执行示例:
SELECT rental_id FROM rental WHERE 1=1 and
rental_date >= '2005-05-25 04:00:00' and
rental_date <= '2005-05-25 05:00:00' and inventory_id=4466;
// 以下语句会输出一个格式为json的跟踪文件
SELECT * FROM information_schema.OPTIMIZER_TRACE;