mysql5.7全局考虑性能化,SQL优化的最后一步:profile性能分析
一、JDBC
setResultSetType(ResultSet.TYPE_FORWARD_ONLY); 告诉mysql发送流数据过来
setFetchSize(1000); 告诉mysql获取条数
拿部分数据过来,直到把所有数据都处理完毕。
用游标也可以。
但上述用游标或者流的方式,都是把压力都转嫁给了mysql,mysql会开辟一个内存出来去放结果集,对mysql而言,会产生极大的压力。那可以考虑比如手动在java层做分页。
二、线程繁忙原因
有时候mysql在执行某SQL时遇上线程繁忙,mysql对于线程状态有细致规定: https://dev.mysql.com/doc/refman/5.7/en/general-thread-states.html
【statistics】 统计。线程正在统计信息以研究查询执行计划。如果线程长时间处于这个状态,意味着这个线程可能被别的磁盘IO占用了。
【Creating tmp table】创建临时表。正在创建一个内存中或磁盘中的临时表,如果刚开始创建的是内存临时表,后来改为创建磁盘临时表,则状态会变为“Coping to tmp table on disk”
我们可以使用 show processlist; 来查看线程处于什么状态:
三、profile性能分析
mysql> select @@have_profiling; -- 查看是否支持profile
+------------------+
| @@have_profiling |
+------------------+
| YES |
+------------------+
1 row in set (0.02 sec)
开启profile:
mysql> select @@profiling; -- 如果支持profile的话,那么查看是否开启了profile, 默认是0不开启,可以通过 set profiling=1; 设置为session级别的开启
+-------------+
| @@profiling |
+-------------+
| 0 |
+-------------+
1 row in set (0.02 sec)
这时候执行一个sql: SELECT * FROM t_user WHERE address in ('shanghaishi', 'beijingshi', 'wenzhoushi'); 结果集大约有8654条数据。再用 show profiles; 命令去看刚才那个SQL的执行耗时情况:
mysql> show profiles;
+----------+------------+-----------------------------------------------------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+-----------------------------------------------------------------------------------+
| 1 | 0.00036600 | select @@profiling |
| 2 | 0.01598275 | select count(*) from t_user |
| 3 | 0.02613600 | SELECT * FROM t_user WHERE address in ('shanghaishi', 'beijingshi', 'wenzhoushi') |
+----------+------------+-----------------------------------------------------------------------------------+
3 rows in set (0.05 sec)
记住这里的Query_ID是3,然后 show profile for query 3;
mysql> show profile for query 3;
+----------------------+----------+
| Status | Duration |
+----------------------+----------+
| starting | 0.000061 |
| checking permissions | 0.000010 |
| checking permissions | 0.000004 |
| Opening tables | 0.000018 |
| init | 0.000075 |
| System lock | 0.000011 |
| optimizing | 0.000014 |
| statistics | 0.000167 |
| preparing | 0.000038 |
| executing | 0.000005 |
| Sending data | 0.025582 |
| end | 0.000012 |
| query end | 0.000007 |
| closing tables | 0.000008 |
| freeing items | 0.000041 |
| logging slow query | 0.000072 |
| cleaning up | 0.000013 |
+----------------------+----------+
17 rows in set (0.05 sec)
以上表示在整个sql的生命周期中,每个阶段的耗时,可以看到耗时最长的环节是 Sending data, 想知道每个环节是什么意思,可以去mysql官网( https://dev.mysql.com/doc/refman/5.7/en/general-thread-states.html )查线程状态,比如Sending data, mysql官网是这么定义的:
翻译过来就是:该线程正在读取和处理 SELECT 语句所检索出来的行,并将数据发送到客户端。由于在此状态期间发生的操作往往会执行大量磁盘访问(读取),因此它通常是给定查询生命周期中运行时间最长的状态。
如果想知道更加具体的Sending data到底慢在cpu? 还是IO?还是其他什么地方,可以使用 show profile all for query 3;
从以上结果集可以看出,大部分时间都花在CPU上。
彩蛋:如果你对mysql的源码感兴趣,可以使用show profile source for query 3; 可以看到具体每个操作定位到mysql源码的哪一行。
end.