目录
一、数据库服务器优化步骤
流程主要分为:观察和行动两部分 S开头为观察,A开头为行动
总体概述:
二、查看系统性能的参数
使用 SHOW STATUS语句查询一些MySQL数据库服务器的性能参数、执行频率。
SHOW [GLOBAL|SESSION] STATUS LIKE '参数';
一些常用的性能参数如下:
Connections:连接MySQL服务器的次数。
Uptime:MySQL服务器的上 线时间。
Slow_queries:慢查询的次数。
Innodb_rows_read:Select查询返回的行数
Innodb_rows_inserted:执行INSERT操作插入的行数
Innodb_rows_updated:执行UPDATE操作更新的 行数
Innodb_rows_deleted:执行DELETE操作删除的行数
Com_select:查询操作的次数。
Com_insert:插入操作的次数。对于批量插入的 INSERT 操作,只累加一次。
Com_update:更新操作 的次数。
Com_delete:删除操作的次数。
#查询MySQL服务器连接次数
SHOW STATUS LIKE 'connections';
#查询mysql服务器工作时间
SHOW STATUS LIKE 'uptime';
#查询MySQL服务器慢查询的次数
SHOW STATUS LIKE 'slow_queries';
三、统计SQL的查询成本:last_query_cost
SHOW STATUS LIKE 'last_query_cost';
结论:①、位置决定效率,页在数据库缓冲池中效率最高,其次是在内存中,最后在磁盘中。
②、批量决定效率:从磁盘中对单一页进行随机读取效率很低,而顺序的批量读取,则效率要更快。
四、定位执行慢的SQL:慢日志查询
MySQL的慢查询日志,记录在MySQL中响应时间超过阈值的语句,以”long_query_time “的默认值10秒标准,超过这个值就是慢SQL。MySQL默认没有开启慢日志查询。调优时开启,不调优则关闭。
4.1、开启并设置慢查询日志查询参数
查询是否开启慢日志查询:
SHOW VARIABLES LIKE '%slow_query_log%';
开启慢日志查询:
SET GLOBAL slow_query_log = ON;
修改long_query_time的阈值
#查询当前阈值
SHOW VARIABLES LIKE '%long_query_time%';
#修改阈值大小
SET GLOBAL long_query_time = 1;
SET long_query_time = 1;
4.2、查看慢查询数目
SHOW GLOBAL STATUS LIKE '%Slow_queries%';
测试表与数据:
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;
DELIMITER //
CREATE PROCEDURE insert_stu1( START INT , max_num INT )
BEGIN
DECLARE 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_num
END REPEAT;
COMMIT; #提交事物
END //
DELIMITER ;
#调用存储函数
CALL insert_stu1(100001,4000000);
SELECT * FROM student WHERE stuno = 3444444;
SELECT * FROM student WHERE NAME = 'xdZMMx';
SHOW STATUS LIKE 'Slow_queries';
4.3、慢查询日志分析工具:mysqldumpslow
mysqldumpslow -help #调取帮助手册
查看有哪些慢查询的SQL
4.4关闭慢查询日志
方式1、永久性关闭
永久开启的需要去配置文件将#slow_query_log =OFF这个配置注释掉 。
方式2:临时方式关闭
SET GLOBAL slow_query_log=off;
4.5、删除慢查询日志
先删除原有的日志,再执行下面命令重新生成慢查询日志
mysqladmin -uroot -p flush-logs slow
五、查看SQL执行成本:SHOW PROFILE
查看最近的开销
SHOW PROFILES;
更详细的信息:
SHOW PROFILE CPU,BLOCK IO FOR QUERY 3;
show profile的常用查询参数:
① ALL:显示所有的开销信息。 ② BLOCK IO:显示块IO开销。 ③ CONTEXT SWITCHES:上下文切换开 销。 ④ CPU:显示CPU开销信息。 ⑤ IPC:显示发送和接收开销信息。 ⑥ MEMORY:显示内存开销信 息。 ⑦ PAGE FAULTS:显示页面错误开销信息。 ⑧ SOURCE:显示和Source_function,Source_file,Source_line相关的开销信息。 ⑨ SWAPS:显示交换次数开销信息。
六、分析查询语句:EXPLAIN
基本语法:
#EXPLAIN和DESCRIBE的功能都是一样的
EXPLAIN SELECT * FROM student_info;
DESCRIBE SELECT * FROM student_info;
数据准备:
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;
#创建函数
DELIMITER //
CREATE FUNCTION rand_string1(n INT)
RETURNS VARCHAR(255) #该函数会返回一个字符串
BEGIN
DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
DECLARE return_str VARCHAR(255) DEFAULT'';
DECLARE i INT DEFAULT 0;
WHILE i < n DO
SET 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))
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET 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_num
END REPEAT;
COMMIT;
END //
DELIMITER ;
DELIMITER //
CREATE PROCEDURE insert_s2 (IN min_num INT (10),IN max_num INT (10))
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET 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_num
END REPEAT;
COMMIT;
END //
DELIMITER ;
CALL insert_s1(10001,10000);
CALL insert_s2(10001,10000);
6.1、explain的字段
6.1.1、table :表名
不论我们的查询语句有多复杂,里边儿 包含了多少个表 ,到最后也是需要对每个表进行 单表访问 的,所 以MySQL规定EXPLAIN语句输出的每条记录都对应着某个单表的访问方法,该条记录的table列代表着该 表的表名(有时不是真实的表名字,可能是简称)。
#查询的每一行记录都对应着一个单表
EXPLAIN SELECT * FROM s1;
#s1:驱动表,s2:被驱动表
EXPLAIN SELECT * FROM s1 INNER JOIN s2;
6.1.2、id
在一个大的查询语句中每个select关键字都对应一个唯一id
#只有一个select
EXPLAIN SELECT * FROM s1 WHERE id = 100045;
#有两个select
EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'a';
特殊情况:
#查询优化器可能对涉及子查询的查询语句进行重写,转为多表查询
EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key2 FROM s2 WHERE common_field = 'a');
#去重建立临时表
EXPLAIN SELECT * FROM s1 UNION SELECT * FROM s2;
EXPLAIN SELECT * FROM s1 UNION ALL SELECT * FROM s2;
总结:
1、id如果相同,可以认为是一组,从上往下顺序执行。
2、在所有组中,id值越大,优先级越高,越先执行。
3、关注点:id号每个号码,表示一趟独立的查询,一个sql的查询趟数越少越好。
6.1.3、select_type:查询类型
MySQL为每一个select关键字代表的小查询都定义了一个称为select_type的属性。
#查询语句中不包括union或子查询都算作是simple类型
EXPLAIN SELECT * FROM s1;
#连接查询也算是simple类型
EXPLAIN SELECT * FROM s1 INNER JOIN s2;
#union与UNION ALL
EXPLAIN SELECT * FROM s1 UNION SELECT * FROM s2;
EXPLAIN SELECT * FROM s1 UNION ALL SELECT * FROM s2;
6.1.4、type
执行计划的一条记录就代表着MySQL对某个表的执行查询时的访问方法,又称”访问类型“。
完整的访问方法如下: system , const , eq_ref , ref , fulltext , ref_or_null ,
index_merge , unique_subquery , index_subquery , range , index , ALL 。
system:只有一条记录并且该表使用的存储引擎的统计数据是精确的。
CREATE TABLE t(i INT) ENGINE=MYISAM;
INSERT INTO t VALUES (1);
EXPLAIN SELECT * FROM t;
const:根据主键或者唯一二级索引列与常数进行等值匹配时
EXPLAIN SELECT * FROM s1 WHERE id = 10005;
eq_ref:连接查询时,如果被驱动表是通过主键或者唯一二级索引列等值匹配的方式进行访问的
EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.id = s2.id;
ref:通过普通的二级索引与常量进行等值匹配时查询某个表。
EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';
ref_or_null:对普通的二级索引进行等值匹配查询,该索引列的值也可以是null值时
EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key1 IS NULL;
index_merge:单表访问方法时在某些场景下可以使用`Intersection`,`Union`,`Sort-Union`这三种索引合并的方式来执行查询。
EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key3 = 'a';
unique_subquery:是针对在一些包含`IN`子查询的查询语句中,如果查询优化器决定将`IN`子查询转换为`exists`子查询,而且子查询可以使用到主键进行等值匹配的话,那么该子查询执行计划`type`列的值就是unique_subquery。
EXPLAIN SELECT * FROM s1 WHERE key2 IN (SELECT id FROM s2 WHERE s1.key1 = s2.key1) OR key3 = 'a';
range:使用索引获取某些范围区间记录
EXPLAIN SELECT * FROM s1 WHERE key1 IN ('a','b','c');
index:使用索引覆盖,但需要扫描全部的索引记录时。
EXPLAIN SELECT key_part2 FROM s1 WHERE key_part3 = 'a';
all:全表扫描
EXPLAIN SELECT * FROM s1;
结果值从最好到最坏依次是: system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL 其中比较重要的几个提取出来(见上图中的蓝 色)。SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,最好是 consts级别。(阿里巴巴 开发手册要求)
6.1.5、possible_keys和key
possible_keys:表示查询语句中,对某个表执行单表查询可能用到的索引。
key :表示实际用到的索引有哪些。
EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND key3 = 'a';
6.1.6、key_len
实际使用到的索引长度。
EXPLAIN SELECT * FROM s1 WHERE id = 10005;
6.1.7、ref
当使用索引列等值查询时,与索引列进行等值匹配的对象信息。
EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';
6.1.8、rows
预估的需要读取的记录条数
EXPLAIN SELECT * FROM s1 WHERE key1 > 'z';
6.1.9、filtered
某个表经过搜索条件过滤后剩余记录条数的百分比
EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND common_field = 'a';
6.1.10、extra
说明一些额外信息,可以更准确的理解MySQL到底将如何执行给定的查询语句
1、查询语句中没有from子句时
EXPLAIN SELECT 1;
2、查询语句的where 子句永远为false时
EXPLAIN SELECT * FROM s1 WHERE 1 != 1;
3、使用全表扫描来执行对某个表的查询,并且该语句的where子句中有针对该表的搜索条件时。
EXPLAIN SELECT * FROM s1 WHERE common_field = 'a';
4、使用索引访问来执行对某个表的查询,并且该语句的where子句中,有除了该索引包含的列之外的其它搜索条件时。
EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' AND common_field = 'a';
5、当查询列表处有min或max聚合函数。但并没有符合where子句中的搜索条件的记录时。
EXPLAIN SELECT MIN(key1) FROM s1 WHERE key1 = 'abcfgj';
6、查询列表及搜索条件中只包含属于某个索引的列,就是可以使用覆盖索引的情况下。
EXPLAIN SELECT key1 FROM s1 WHERE key1 = 'a';
7、搜索条件中虽然有索引列,但却不能使用到索引
EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND key1 LIKE '%a';
8、在连接查询执行过程中,当被驱动表不能有效的利用索引加快访问速度,MySQL会分配一块名为 join buffer的内存来加快查询速度。
EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.common_field = s2.common_field;
总结:
1、EXPLAIN不考虑各种Cache。
2、EXPLAIN不能显示MySQL在执行查询时所作的优化工作。
3、EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况。
4、部分统计信息是估算的,并非精确值。
6.2、explain的使用
EXPLAIN可以输出四种格式: 传统格式 , JSON格式 , TREE格式 以及可视化输出 。用户可以根据需要选择适用于自己的格式。
6.2.1、传统格式
EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.common_field = s2.common_field;
6.2.2、json格式
在EXPLAIN单词和真正的查询语句中间加上 FORMAT=JSON 。
EXPLAIN FORMAT=json SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key2 WHERE s1.common_field = 'a'\G;
6.2.3、tree格式
TREE格式是8.0.16版本之后引入的新格式,主要根据查询的 各个部分之间的关系 和 各部分的执行顺序 来描 述如何查询。
EXPLAIN FORMAT=tree SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key2 WHERE s1.common_field = 'a'\G;
6.3、SHOW WARNINGS的使用
EXPLAIN SELECT s1.key1, s2.key1 FROM s1 LEFT JOIN s2 ON s1.key1 = s2.key1 WHERE s2.common_field IS NOT NULL;
SHOW WARNINGS;
七、分析优化器执行计划:trace
#开启该功能并设置为json格式
SET optimizer_trace="enabled=on",end_markers_in_json=ON;
#设置内存大小
SET optimizer_trace_max_mem_size=1000000;
测试使用:
SELECT * FROM student WHERE id < 10;
SELECT * FROM information_schema.optimizer_trace
//第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,
9. MySQL监控分析视图-sys schema
"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)
八、MySQL监控分析视图-sys schema
8.1、Sys schema视图摘要
1. 主机相关:以host_summary开头,主要汇总了IO延迟的信息。
2. Innodb相关:以innodb开头,汇总了innodb buffer信息和事务等待innodb锁的信息。
3. I/o相关:以io开头,汇总了等待I/O、I/O使用量情况。
4. 内存使用情况:以memory开头,从主机、线程、事件等角度展示内存的使用情况
5. 连接与会话信息:processlist和session相关视图,总结了会话相关信息。
6. 表相关:以schema_table开头的视图,展示了表的统计信息。
7. 索引信息:统计了索引的使用情况,包含冗余索引和未使用的索引情况。
8. 语句相关:以statement开头,包含执行全表扫描、使用临时表、排序等的语句信息。
9. 用户相关:以user开头的视图,统计了用户使用的文件I/O、执行语句统计信息。
10. 等待事件相关信息:以wait开头,展示等待事件的延迟情况。
8.2、Sys schema视图使用场景
8.2.1、索引情况
#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_deleted
FROM sys.schema_index_statistics WHERE table_schema='dbname' ;
8.2.2、表相关
# 1. 查询表的访问量
SELECT table_schema,TABLE_NAME,SUM(io_read_requests+io_write_requests) AS IO FROM
sys.schema_table_statistics GROUP BY table_schema,TABLE_NAME ORDER BY IO DESC;
# 2. 查询占用bufferpool较多的表
SELECT object_schema,object_name,allocated,DATA
FROM sys.innodb_buffer_stats_by_table ORDER BY allocated LIMIT 10;
# 3. 查看表的全表扫描情况
SELECT * FROM sys.statements_with_full_table_scans WHERE db='dbname';
8.2.3、语句相关
#1. 监控SQL执行的频率
select db,exec_count,query from sys.statement_analysis
order by exec_count desc;
#2. 监控使用了排序的SQL
select db,exec_count,first_seen,last_seen,query
from sys.statements_with_sorting limit 1;
#3. 监控使用了临时表或者磁盘临时表的SQL
select db,exec_count,tmp_tables,tmp_disk_tables,query
from sys.statement_analysis where tmp_tables>0 or tmp_disk_tables >0
order by (tmp_tables+tmp_disk_tables) desc;
8.2.4、IO相关
#1. 查看消耗磁盘IO的文件
select file,avg_read,avg_write,avg_read+avg_write as avg_io
from sys.io_global_by_file_by_bytes order by avg_read limit 10;
8.2.5、Innodb 相关
#1. 行锁阻塞情况
select * from sys.innodb_lock_waits;