MySQL高级_第09章_性能分析工具的使用
1. 数据库服务器的优化步骤
2. 查看系统性能参数
SHOW [ GLOBAL | SESSION ] STATUS LIKE ' 参数 ' ;
3. 统计SQL的查询成本:last_query_cost
CREATE TABLE `student_info` (`id` INT ( 11 ) NOT NULL AUTO_INCREMENT ,`student_id` INT NOT NULL ,`name` VARCHAR ( 20 ) DEFAULT NULL ,`course_id` INT NOT NULL ,`class_id` INT ( 11 ) DEFAULT NULL ,`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,PRIMARY KEY ( `id` )) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET =utf8;
SELECT student_id, class_id, NAME, create_time FROM student_infoWHERE id = 900001 ;
mysql> SHOW STATUS LIKE 'last_query_cost' ;+-----------------+----------+| Variable_name | Value |+-----------------+----------+| Last_query_cost | 1.000000 |+-----------------+----------+
SELECT student_id, class_id, NAME, create_time FROM student_infoWHERE id BETWEEN 900001 AND 900100 ;
mysql> SHOW STATUS LIKE 'last_query_cost' ;+-----------------+-----------+| Variable_name | Value |+-----------------+-----------+| Last_query_cost | 21.134453 |+-----------------+-----------+
4. 定位执行慢的 SQL:慢查询日志
4.1 开启慢查询日志参数
mysql > set global slow_query_log= 'ON' ;
mysql > show variables like '%long_query_time%' ;
# 测试发现:设置 global 的方式对当前 session 的 long_query_time 失效。对新连接的客户端有效。所以可以一并执行下述语句mysql > set global long_query_time = 1 ;mysql> show global variables like '%long_query_time%' ;mysql> set long_query_time= 1 ;mysql> show variables like '%long_query_time%' ;
4.2 查看慢查询数目
SHOW GLOBAL STATUS LIKE '%Slow_queries%' ;
4.3 案例演示
CREATE TABLE `student` (`id` INT ( 11 ) NOT NULL AUTO_INCREMENT ,`stuno` INT NOT NULL ,`name` VARCHAR ( 20 ) DEFAULT NULL ,`age` INT ( 3 ) DEFAULT NULL ,`classId` INT ( 11 ) DEFAULT NULL ,PRIMARY KEY ( `id` )) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET =utf8;
This function has none of DETERMINISTIC......
set global log_bin_trust_function_creators= 1 ; # 不加 global 只是当前窗口有效。
DELIMITER //CREATE FUNCTION rand_string(n INT )RETURNS VARCHAR ( 255 ) # 该函数会返回一个字符串BEGINDECLARE chars_str VARCHAR ( 100 ) DEFAULT'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ' ;DECLARE return_str VARCHAR ( 255 ) DEFAULT '' ;DECLARE i INT DEFAULT 0 ;WHILE i < n DOSET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR( 1 +RAND()* 52 ), 1 ));SET i = i + 1 ;END WHILE ;RETURN return_str;END //DELIMITER ;# 测试SELECT rand_string( 10 );
DELIMITER //CREATE FUNCTION rand_num (from_num INT ,to_num INT ) RETURNS INT ( 11 )BEGINDECLARE i INT DEFAULT 0 ;SET i = FLOOR(from_num +RAND()*(to_num - from_num+ 1 )) ;RETURN i;END //DELIMITER ;# 测试:SELECT rand_num( 10 , 100 );
DELIMITER //CREATE PROCEDURE insert_stu1( START INT , max_num INT )BEGINDECLARE i INT DEFAULT 0 ;SET autocommit = 0 ; # 设置手动提交事务REPEAT # 循环SET i = i + 1 ; # 赋值INSERT INTO student (stuno, NAME ,age ,classId ) VALUES(( START +i),rand_string( 6 ),rand_num( 10 , 100 ),rand_num( 10 , 1000 ));UNTIL i = max_numEND REPEAT ;COMMIT ; # 提交事务END //DELIMITER ;
# 调用刚刚写好的函数 , 4000000 条记录 , 从 100001 号开始CALL insert_stu1( 100001 , 4000000 );
4.4 测试及分析
mysql> SELECT * FROM student WHERE stuno = 3455655 ;+---------+---------+--------+------+---------+| id | stuno | name | age | classId |+---------+---------+--------+------+---------+| 3523633 | 3455655 | oQmLUr | 19 | 39 |+---------+---------+--------+------+---------+1 row in set ( 2.09 sec)mysql> SELECT * FROM student WHERE name = 'oQmLUr' ;+---------+---------+--------+------+---------+| id | stuno | name | age | classId |+---------+---------+--------+------+---------+| 1154002 | 1243200 | OQMlUR | 266 | 28 || 1405708 | 1437740 | OQMlUR | 245 | 439 || 1748070 | 1680092 | OQMlUR | 240 | 414 || 2119892 | 2051914 | oQmLUr | 17 | 32 || 2893154 | 2825176 | OQMlUR | 245 | 435 || 3523633 | 3455655 | oQmLUr | 19 | 39 |+---------+---------+--------+------+---------+6 rows in set ( 2.39 sec)
show status like 'slow_queries' ;
4.5 慢查询日志分析工具:mysqldumpslow
mysqldumpslow -- help
- -a: 不将数字抽象成N,字符串抽象成S
- -s: 是表示按照何种方式排序:
- c: 访问次数
- l: 锁定时间
- r: 返回记录
- t: 查询时间
- al:平均锁定时间
- ar:平均返回记录数
- at:平均查询时间 (默认方式)
- ac:平均查询次数
- -t: 即为返回前面多少条的数据;
- -g: 后边搭配一个正则匹配模式,大小写不敏感的;
mysqldumpslow -s t -t 5 /var/lib/mysql/atguigu01- slow .log
[root @bogon ~] # mysqldumpslow -s t -t 5 /var/lib/mysql/atguigu01-slow.logReading mysql slow query log from /var/lib/mysql/atguigu01- slow .logCount : 1 Time = 2.39 s ( 2 s) Lock = 0.00 s ( 0 s) Rows= 13.0 ( 13 ), root[root] @localhostSELECT * FROM student WHERE name = 'S'Count : 1 Time = 2.09 s ( 2 s) Lock = 0.00 s ( 0 s) Rows= 2.0 ( 2 ), root[root] @localhostSELECT * FROM student WHERE stuno = NDied at /usr/bin/mysqldumpslow line 162 , <> chunk 2.
# 得到返回记录集最多的 10 个 SQLmysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log# 得到访问次数最多的 10 个 SQLmysqldumpslow -s c -t 10 /var/lib/mysql/atguigu-slow.log# 得到按照时间排序的前 10 条里面含有左连接的查询语句mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/atguigu-slow.log# 另外建议在使用这些命令时结合 | 和 more 使用 ,否则有可能出现爆屏情况mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log | more
4.6 关闭慢查询日志
[mysqld]slow_query_log = OFF
SHOW VARIABLES LIKE '%slow%' ; # 查询慢查询日志所在目录SHOW VARIABLES LIKE '%long_query_time%' ; # 查询超时时长
SET GLOBAL slow_query_log=off;
SHOW VARIABLES LIKE '%slow%' ;# 以及SHOW VARIABLES LIKE '%long_query_time%' ;
4.7 删除慢查询日志
5. 查看 SQL 执行成本:SHOW PROFILE
mysql > show variables like 'profiling';
mysql > set profiling = 'ON' ;
mysql > show profiles ;
mysql > show profile ;
mysql> show profile cpu,block io for query 2 ;
6. 分析查询语句:EXPLAIN
6.1 概述
- MySQL 5.6.3以前只能 EXPLAIN SELECT ;MYSQL 5.6.3以后就可以 EXPLAIN SELECT,UPDATE, DELETE
- 在5.7以前的版本中,想要显示 partitions 需要使用 explain partitions 命令;想要显示 filtered 需要使用 explain extended 命令。在5.7版本后,默认explain直接显示partitions和 filtered中的信息。
6.2 基本语法
EXPLAIN 或 DESCRIBE语句的语法形式如下:
EXPLAIN SELECT select_options或者DESCRIBE SELECT select_options
如果我们想看看某个查询的执行计划的话,可以在具体的查询语句前边加一个 EXPLAIN ,就像这样:
mysql> EXPLAIN SELECT 1 ;
列名
|
描述
|
id
|
在一个大的查询语句中每个
SELECT
关键字都对应一个
唯一的
id
|
select_type
|
SELECT
关键字对应的那个查询的类型
|
table
|
表名
|
partitions
|
匹配的分区信息
|
type
|
针对单表的访问方法
|
possible_keys
|
可能用到的索引
|
key
|
实际上使用的索引
|
key_len
|
实际使用到的索引长度
|
ref
|
当使用索引列等值查询时,与索引列进行等值匹配的对象信息
|
rows
|
预估的需要读取的记录条数
|
filtered
|
某个表经过搜索条件过滤后剩余记录条数的百分比
|
Extra
|
一些额外的信息
|
6.3 数据准备
CREATE TABLE s1 (id INT AUTO_INCREMENT ,key1 VARCHAR ( 100 ),key2 INT ,key3 VARCHAR ( 100 ),key_part1 VARCHAR ( 100 ),key_part2 VARCHAR ( 100 ),key_part3 VARCHAR ( 100 ),common_field VARCHAR ( 100 ),PRIMARY KEY (id),INDEX idx_key1 (key1),UNIQUE INDEX idx_key2 (key2),INDEX idx_key3 (key3),INDEX idx_key_part(key_part1, key_part2, key_part3)) ENGINE = INNODB CHARSET =utf8;
CREATE TABLE s2 (id INT AUTO_INCREMENT ,key1 VARCHAR ( 100 ),key2 INT ,key3 VARCHAR ( 100 ),key_part1 VARCHAR ( 100 ),key_part2 VARCHAR ( 100 ),key_part3 VARCHAR ( 100 ),common_field VARCHAR ( 100 ),PRIMARY KEY (id),INDEX idx_key1 (key1),UNIQUE INDEX idx_key2 (key2),INDEX idx_key3 (key3),INDEX idx_key_part(key_part1, key_part2, key_part3)) ENGINE = INNODB CHARSET =utf8;
set global log_bin_trust_function_creators= 1 ; # 不加 global 只是当前窗口有效。
DELIMITER //CREATE FUNCTION rand_string1(n INT )RETURNS VARCHAR ( 255 ) # 该函数会返回一个字符串BEGINDECLARE chars_str VARCHAR ( 100 ) DEFAULT'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ' ;DECLARE return_str VARCHAR ( 255 ) DEFAULT '' ;DECLARE i INT DEFAULT 0 ;WHILE i < n DOSET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR( 1 +RAND()* 52 ), 1 ));SET i = i + 1 ;END WHILE ;RETURN return_str;END //DELIMITER ;
DELIMITER //CREATE PROCEDURE insert_s1 ( IN min_num INT ( 10 ), IN max_num INT ( 10 ))BEGINDECLARE i INT DEFAULT 0 ;SET autocommit = 0 ;REPEATSET i = i + 1 ;INSERT INTO s1 VALUES ((min_num + i),rand_string1( 6 ),(min_num + 30 * i + 5 ),rand_string1( 6 ),rand_string1( 10 ),rand_string1( 5 ),rand_string1( 10 ),rand_string1( 10 ));UNTIL i = max_numEND REPEAT ;COMMIT ;END //DELIMITER ;
DELIMITER //CREATE PROCEDURE insert_s2 ( IN min_num INT ( 10 ), IN max_num INT ( 10 ))BEGINDECLARE i INT DEFAULT 0 ;SET autocommit = 0 ;REPEATSET i = i + 1 ;INSERT INTO s2 VALUES ((min_num + i),rand_string1( 6 ),(min_num + 30 * i + 5 ),rand_string1( 6 ),rand_string1( 10 ),rand_string1( 5 ),rand_string1( 10 ),rand_string1( 10 ));UNTIL i = max_numEND REPEAT ;COMMIT ;END //DELIMITER ;
CALL insert_s1( 10001 , 10000 );
CALL insert_s2( 10001 , 10000 );
6.4 EXPLAIN各列作用
1. table
2. id
SELECT * FROM s1 WHERE key1 = 'a' ;
SELECT * FROM s1 INNER JOIN s2ON s1 .key 1 = s2 .key 1WHERE s1 .common_field = 'a' ;
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' ;
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2;
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'a';
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key2 FROM s2 WHERE common_field = 'a');
mysql> EXPLAIN SELECT * FROM s1 UNION SELECT * FROM s2;
mysql> EXPLAIN SELECT * FROM s1 UNION ALL SELECT * FROM s2;
- id如果相同,可以认为是一组,从上往下顺序执行
- 在所有组中,id值越大,优先级越高,越先执行
- 关注点:id号每个号码,表示一趟独立的查询, 一个sql的查询趟数越少越好
3. select_type
具体分析如下:
mysql> EXPLAIN SELECT * FROM s1;
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2;
- PRIMARY
mysql> EXPLAIN SELECT * FROM s1 UNION SELECT * FROM s2;
- UNION
- UNION RESULT
- SUBQUERY
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN ( SELECT key1 FROM s2) OR key3 = 'a' ;
- DEPENDENT SUBQUERY
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN ( SELECT key1 FROM s2 WHERE s1 .key 2 = s2 .key 2 ) OR key3 = 'a' ;
- DEPENDENT UNION
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN ( SELECT key1 FROM s2 WHERE key1 = 'a' UNION SELECT key1 FROM s1 WHERE key1 = 'b' );
- DERIVED
mysql> EXPLAIN SELECT * FROM ( SELECT key1, count (*) as c FROM s1 GROUP BY key1) AS derived_s1 where c > 1 ;
- MATERIALIZED
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2);
- UNCACHEABLE SUBQUERY
- UNCACHEABLE UNION
4. partitions (可略)
-- 创建分区表,-- 按照 id 分区, id<100 p0 分区,其他 p1 分区CREATE TABLE user_partitions (id INT auto_increment ,NAME VARCHAR ( 12 ), PRIMARY KEY (id))PARTITION BY RANGE (id)(PARTITION p0 VALUES less than( 100 ),PARTITION p1 VALUES less than MAXVALUE);
DESC SELECT * FROM user_partitions WHERE id>200;
5. type ☆
- system
mysql> CREATE TABLE t(i int ) Engine =MyISAM;Query OK, 0 rows affected ( 0.05 sec)mysql> INSERT INTO t VALUES ( 1 );Query OK, 1 row affected ( 0.01 sec)
mysql> EXPLAIN SELECT * FROM t;
- const
mysql> EXPLAIN SELECT * FROM s1 WHERE id = 10005;
- eq_ref
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.id = s2.id;
从执行计划的结果中可以看出,MySQL打算将s2作为驱动表,s1作为被驱动表,重点关注s1的访问方法是 eq_ref ,表明在访问s1表的时候可以 通过主键的等值匹配 来进行访问。
- ref
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' ;
- fulltext
- ref_or_null
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key1 IS NULL ;
- index_merge
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key3 = 'a' ;
- unique_subquery
mysql> EXPLAIN SELECT * FROM s1 WHERE key2 IN ( SELECT id FROM s2 where s1 .key 1 =s2 .key 1 ) OR key3 = 'a' ;
- index_subquery
mysql> EXPLAIN SELECT * FROM s1 WHERE common_field IN ( SELECT key3 FROM s2 where s1 .key 1 = s2 .key 1 ) OR key3 = 'a' ;
- range
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN ( 'a' , 'b' , 'c' );
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 > 'a' AND key1 < 'b' ;
- index
mysql> EXPLAIN SELECT key_part2 FROM s1 WHERE key_part3 = 'a' ;
- ALL
6. possible_keys和key
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND key3 = 'a';
mysql> EXPLAIN SELECT * FROM s1 WHERE id = 10005 ;
mysql> EXPLAIN SELECT * FROM s1 WHERE key2 = 10126 ;
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' ;
mysql> EXPLAIN SELECT * FROM s1 WHERE key_part1 = 'a' ;
mysql> EXPLAIN SELECT * FROM s1 WHERE key_part1 = 'a' AND key_part2 = 'b';
varchar(10) 变长字段且允许 NULL = 10 * ( character set :utf8=3,gbk=2,latin1=1)+1(NULL)+2( 变长字段 )varchar(10) 变长字段且不允许 NULL = 10 * ( character set : utf8=3,gbk=2,latin1=1)+2( 变长字段 )char(10) 固定字段且允许 NULL = 10 * ( character set : utf8=3,gbk=2,latin1=1)+1(NULL)char(10) 固定字段且不允许 NULL = 10 * ( character set : utf8=3,gbk=2,latin1=1)
8. ref
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.id = s2.id;
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s2.key1 = UPPER(s1.key1);
9. rows ☆
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 > 'z';
10. filtered
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND common_field = 'a';
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1 .key 1 = s2 .key 1 WHEREs1 .common_field = 'a' ;
11. Extra ☆
mysql> EXPLAIN SELECT 1;
- Impossible WHERE
mysql> EXPLAIN SELECT * FROM s1 WHERE 1 != 1;
- Using where
mysql> EXPLAIN SELECT * FROM s1 WHERE common_field = 'a';
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' AND common_field = 'a' ;
- No matching min/max row
mysql> EXPLAIN SELECT MIN (key1) FROM s1 WHERE key1 = 'abcdefg' ;
- Using index
mysql> EXPLAIN SELECT key1 FROM s1 WHERE key1 = 'a' ;
- Using index condition
SELECT * FROM s1 WHERE key1 > 'z' AND key1 LIKE '%a' ;
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND key1 LIKE '%b' ;
- Using join buffer (Block Nested Loop)
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1 .common_field =s2 .common_field ;
- Not exists
mysql> EXPLAIN SELECT * FROM s1 LEFT JOIN s2 ON s1 .key 1 = s2 .key 1 WHERE s2 .id ISNULL ;
- Using intersect(...) 、 Using union(...) 和 Using sort_union(...)
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key3 = 'a' ;
- Zero limit
mysql> EXPLAIN SELECT * FROM s1 LIMIT 0 ;
- Using filesort
mysql> EXPLAIN SELECT * FROM s1 ORDER BY key1 LIMIT 10 ;
mysql> EXPLAIN SELECT * FROM s1 ORDER BY common_field LIMIT 10 ;
- Using temporary
mysql> EXPLAIN SELECT DISTINCT common_field FROM s1;
mysql> EXPLAIN SELECT common_field, COUNT (*) AS amount FROM s1 GROUP BYcommon_field;
mysql> EXPLAIN SELECT key1, COUNT(*) AS amount FROM s1 GROUP BY key1;
从 Extra 的 Using index 的提示里我们可以看出,上述查询只需要扫描 idx_key1 索引就可以搞 定了,不再需要临时表了。
- 其它
12. 小结
- EXPLAIN不考虑各种Cache
- EXPLAIN不能显示MySQL在执行查询时所作的优化工作
- EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况
- 部分统计信息是估算的,并非精确值
7. EXPLAIN的进一步使用
7.1 EXPLAIN四种输出格式
1. 传统格式
mysql> EXPLAIN SELECT s1 .key 1 , s2 .key 1 FROM s1 LEFT JOIN s2 ON s1 .key 1 = s2 .key 1 WHEREs2 .common_field IS NOT NULL ;
2. JSON格式
- JSON格式:在EXPLAIN单词和真正的查询语句中间加上 FORMAT=JSON 。
EXPLAIN FORMAT=JSON SELECT ....
"cost_info" : {"read_cost" : "1840.84" ,"eval_cost" : "193.76" ,"prefix_cost" : "2034.60" ,"data_read_per_join" : "1M"}
- read_cost 是由下边这两部分组成的:
- IO 成本
- 检测 rows × (1 - filter) 条记录的 CPU 成本
小贴士:rows 和 filter 都是我们前边介绍执行计划的输出列,在 JSON 格式的执行计划中, rows相当于 rows_examined_per_scan , filtered 名称不变。
- eval_cost 是这样计算的:
- prefix_cost 就是单独查询 s1 表的成本,也就是:
- data_read_per_join 表示在此次查询中需要读取的数据量。
"cost_info" : {"read_cost" : "968.80" ,"eval_cost" : "193.76" ,"prefix_cost" : "3197.16" ,"data_read_per_join" : "1M"}
968.80 + 193.76 + 2034.60 = 3197.16
3. TREE格式
mysql> EXPLAIN FORMAT=tree SELECT * FROM s1 INNER JOIN s2 ON s1 .key 1 = s2 .key 2 WHEREs1 .common_field = 'a' \G*************************** 1. row ***************************EXPLAIN : -> Nested loop inner join (cost= 1360.08 rows= 990 )-> Filter: ((s1 .common_field = 'a' ) and (s1 .key 1 is not null )) (cost= 1013.75rows= 990 )-> Table scan on s1 (cost= 1013.75 rows= 9895 )-> Single- row index lookup on s2 using idx_key2 (key2=s1 .key 1 ), with indexcondition : (cast(s1 .key 1 as double ) = cast(s2 .key 2 as double )) (cost= 0.25 rows= 1 )1 row in set , 1 warning ( 0.00 sec)
4. 可视化输出
7.2 SHOW WARNINGS的使用
mysql> EXPLAIN SELECT s1 .key 1 , s2 .key 1 FROM s1 LEFT JOIN s2 ON s1 .key 1 = s2 .key 1 WHEREs2 .common_field IS NOT NULL ;
mysql> SHOW WARNINGS \G*************************** 1. row ***************************Level : NoteCode : 1003Message: /* select#1 */ select `atguigu` . `s1` . `key1` AS `key1` , `atguigu` . `s2` . `key1`AS `key1` from `atguigu` . `s1` join `atguigu` . `s2` where (( `atguigu` . `s1` . `key1` =`atguigu` . `s2` . `key1` ) and ( `atguigu` . `s2` . `common_field` is not null ))1 row in set ( 0.00 sec)
8. 分析优化器执行计划:trace
SET optimizer_trace= "enabled=on" ,end_markers_in_json= on ;set optimizer_trace_max_mem_size= 1000000 ;
- SELECT
- INSERT
- REPLACE
- UPDATE
- DELETE
- EXPLAIN
- SET
- DECLARE
- CASE
- IF
- RETURN
- CALL
select * from student where id < 10 ;
select * from information_schema .optimizer_trace\G
*************************** 1. row ***************************// 第 1 部分:查询语句QUERY : select * from student where id < 10// 第 2 部分: QUERY 字段对应语句的跟踪信息TRACE : {"steps" : [{"join_preparation" : { // 预备工作"select#" : 1 ,"steps" : [{"expanded_query" : "/* select#1 */ select `student`.`id` AS`id`,`student`.`stuno` AS `stuno`,`student`.`name` AS `name`,`student`.`age` AS`age`,`student`.`classId` AS `classId` from `student` where (`student`.`id` < 10)"}] /* steps */} /* join_preparation */},{"join_optimization" : { // 进行优化"select#" : 1 ,"steps" : [{"condition_processing" : { // 条件处理"condition" : "WHERE" ,"original_condition" : "(`student`.`id` < 10)" ,"steps" : [{"transformation" : "equality_propagation" ,"resulting_condition" : "(`student`.`id` < 10)"},{"transformation" : "constant_propagation" ,"resulting_condition" : "(`student`.`id` < 10)"},{"transformation" : "trivial_condition_removal" ,"resulting_condition" : "(`student`.`id` < 10)"}] /* steps */} /* condition_processing */},{"substitute_generated_columns" : { // 替换生成的列} /* substitute_generated_columns */},{"table_dependencies" : [ // 表的依赖关系{"table" : "`student`" ,"row_may_be_null" : false ,"map_bit" : 0 ,"depends_on_map_bits" : [] /* depends_on_map_bits */}] /* table_dependencies */},{"ref_optimizer_key_uses" : [ // 使用键] /* ref_optimizer_key_uses */},{"rows_estimation" : [ // 行判断{"table" : "`student`" ,"range_analysis" : {"table_scan" : {"rows" : 3973767 ,"cost" : 408558} /* table_scan */ , // 扫描表"potential_range_indexes" : [ // 潜在的范围索引{"index" : "PRIMARY" ,"usable" : true ,"key_parts" : ["id"] /* key_parts */}] /* potential_range_indexes */ ,"setup_range_conditions" : [ // 设置范围条件] /* setup_range_conditions */ ,"group_index_range" : {"chosen" : false ,"cause" : "not_group_by_or_distinct"} /* group_index_range */ ,"skip_scan_range" : {"potential_skip_scan_indexes" : [{"index" : "PRIMARY" ,"usable" : false ,"cause" : "query_references_nonkey_column"}] /* potential_skip_scan_indexes */} /* skip_scan_range */ ,"analyzing_range_alternatives" : { // 分析范围选项"range_scan_alternatives" : [{"index" : "PRIMARY" ,"ranges" : ["id < 10"] /* ranges */ ,"index_dives_for_eq_ranges" : true ,"rowid_ordered" : true ,"using_mrr" : false ,"index_only" : false ,"rows" : 9 ,"cost" : 1.91986 ,"chosen" : true}] /* range_scan_alternatives */ ,"analyzing_roworder_intersect" : {"usable" : false ,"cause" : "too_few_roworder_scans"} /* analyzing_roworder_intersect */} /* analyzing_range_alternatives */ ,"chosen_range_access_summary" : { // 选择范围访问摘要"range_access_plan" : {"type" : "range_scan" ,"index" : "PRIMARY" ,"rows" : 9 ,"ranges" : ["id < 10"] /* ranges */} /* range_access_plan */ ,"rows_for_plan" : 9 ,"cost_for_plan" : 1.91986 ,"chosen" : true} /* chosen_range_access_summary */} /* range_analysis */}] /* rows_estimation */},{"considered_execution_plans" : [ // 考虑执行计划{"plan_prefix" : [] /* plan_prefix */ ,"table" : "`student`" ,"best_access_path" : { // 最佳访问路径"considered_access_paths" : [{"rows_to_scan" : 9 ,"access_type" : "range" ,"range_details" : {"used_index" : "PRIMARY"} /* range_details */ ,"resulting_rows" : 9 ,"cost" : 2.81986 ,"chosen" : true}] /* considered_access_paths */} /* best_access_path */ ,"condition_filtering_pct" : 100 , // 行过滤百分比"rows_for_plan" : 9 ,"cost_for_plan" : 2.81986 ,"chosen" : true}] /* considered_execution_plans */},{"attaching_conditions_to_tables" : { // 将条件附加到表上"original_condition" : "(`student`.`id` < 10)" ,"attached_conditions_computation" : [] /* attached_conditions_computation */ ,"attached_conditions_summary" : [ // 附加条件概要{"table" : "`student`" ,"attached" : "(`student`.`id` < 10)"}] /* attached_conditions_summary */} /* attaching_conditions_to_tables */},{"finalizing_table_conditions" : [{"table" : "`student`" ,"original_table_condition" : "(`student`.`id` < 10)" ,"final_table_condition " : "(`student`.`id` < 10)"}] /* finalizing_table_conditions */},{"refine_plan" : [ // 精简计划{"table" : "`student`"}] /* refine_plan */}] /* steps */} /* join_optimization */},{"join_execution" : { // 执行"select#" : 1 ,"steps" : [] /* steps */} /* join_execution */}] /* steps */}// 第 3 部分:跟踪信息过长时,被截断的跟踪信息的字节数。MISSING_BYTES_BEYOND_MAX_MEM_SIZE : 0 // 丢失的超出最大容量的字节// 第 4 部分:执行跟踪语句的用户是否有查看对象的权限。当不具有权限时,该列信息为 1 且 TRACE 字段为空,一般在调用带有 SQL SECURITY DEFINER 的视图或者是存储过程的情况下,会出现此问题。INSUFFICIENT_PRIVILEGES : 0 // 缺失权限1 row in set ( 0.00 sec )
9. MySQL监控分析视图-sys schema
9.1 Sys schema视图摘要
9.2 Sys schema视图使用场景
#1. 查询冗余索引select * from sys .schema_redundant_indexes ;#2. 查询未使用过的索引select * from sys .schema_unused_indexes ;#3. 查询索引的使用情况select index_name,rows_selected,rows_inserted,rows_updated,rows_deletedfrom sys .schema_index_statistics where table_schema= 'dbname' ;
# 1. 查询表的访问量select table_schema, table_name , sum (io_read_requests+io_write_requests) as io fromsys .schema_table_statistics group by table_schema, table_name order by io desc ;# 2. 查询占用 bufferpool 较多的表select object_schema,object_name,allocated, datafrom sys .innodb_buffer_stats_by_table order by allocated limit 10 ;# 3. 查看表的全表扫描情况select * from sys .statements_with_full_table_scans where db= 'dbname' ;
#1. 监控 SQL 执行的频率select db,exec_count, query from sys .statement_analysisorder by exec_count desc ;#2. 监控使用了排序的 SQLselect db,exec_count,first_seen,last_seen, queryfrom sys .statements_with_sorting limit 1 ;#3. 监控使用了临时表或者磁盘临时表的 SQLselect db,exec_count,tmp_tables,tmp_disk_tables, queryfrom sys .statement_analysis where tmp_tables> 0 or tmp_disk_tables > 0order by (tmp_tables+tmp_disk_tables) desc ;
#1. 查看消耗磁盘 IO 的文件select file,avg_read,avg_write,avg_read+avg_write as avg_iofrom sys .io_global_by_file_by_bytes order by avg_read limit 10 ;
#1. 行锁阻塞情况select * from sys .innodb_lock_waits ;