《高性能mysql》笔记-服务器性能剖析总结
如果遇到了性能需要优化的问题时,首先明确目标对象是什么?是整个应用程序还是数据库。还要明确的是性能衡量的单位是每个查询所花费的时间。即响应时间。响应时间又分为等待时间和执行时间。而不是cpu利用率,吞吐量。接下来是测量每个步骤所需的时间。若是数据库优化,则首先要判断是服务器问题还是单条sql查询的问题。判断的步骤如下:
1. 利用SHOW GLOBAL STATUS
主要查看Threads_running和Queries参数是否突然出现峰值或者低谷。当查询线程数增加时,而Queries查询条数变少了。这种险象现象有两种可能性,缓存失效和大量锁资源。
2.SHOW PROCESSLISTS
所有线程列表。查看是否有大量的线程处于不正常的状态或其他不正常的特征。查询很少会长时间处于statistics状态。或者长时间处于lock状态
3.慢查询日志
可以定位查询响应时间长的语句。
定位到了是sql查询到问题时,对单条sql查询进行优化。
SHOW PROFILES
首先用SHOW PROFILES看查询sql每个步骤执行时间。若有些步骤所需要的时间较久,接下来我们需要知道为什么所需时间这么长。
默认禁用,所以要set profiling = 1;
set profiling=1
select * from t_Order;
select * from t_Product
show profiles
+----------+------------+-------------------------+
| Query_ID | Duration | Query |
+----------+------------+-------------------------+
| 1 | 9.75e-05 | SHOW WARNINGS |
| 2 | 0.00052075 | select * from t_order |
| 3 | 0.000511 | select * from t_product |
| 4 | 5.3e-05 | SHOW WARNINGS |
+----------+------------+-------------------------+
show profile for query 3
+----------------------+----------+
| Status | Duration |
+----------------------+----------+
| starting | 0.000065 |
| checking permissions | 0.000009 |
| Opening tables | 0.000142 |
| init | 0.000022 |
| System lock | 0.000010 |
| optimizing | 0.000008 |
| statistics | 0.000013 |
| preparing | 0.000012 |
| executing | 0.000007 |
| Sending data | 0.000154 |
| end | 0.000010 |
| query end | 0.000011 |
| closing tables | 0.000010 |
| freeing items | 0.000016 |
| cleaning up | 0.000012 |
+----------------------+----------+
因为剖析报告中给出的是每个步骤的时间。看结果很难发现哪个步骤花费的时间较多。最好的办法是按照花费的时间进行排序。我们看用INFOMATION_SHCEMA查看
set @query_id=1
SELECT STATE,SUM(DURATION) AS Total_R,
ROUND(
100*SUM(DURATION)/(SELECT SUM(DURATION) FROM INFORMATION_SCHEMA.PROFILING WHERE QUERY_ID = @query_id),2
) AS Pct_R,COUNT(*) AS Calls,SUM(DURATION)/COUNT(*) AS "R/Call"
FROM INFORMATION_SCHEMA.PROFILING
WHERE QUERY_ID=@query_id
GROUP BY STATE
ORDER BY Total_R DESC
# 输出如下:
+----------------------+----------+-------+-------+--------------+
| STATE | Total_R | Pct_R | Calls | R/Call |
+----------------------+----------+-------+-------+--------------+
| starting | 0.000072 | 20.45 | 1 | 0.0000720000 |
| Sending data | 0.000047 | 13.35 | 1 | 0.0000470000 |
| init | 0.000030 | 8.52 | 1 | 0.0000300000 |
| Opening tables | 0.000026 | 7.39 | 1 | 0.0000260000 |
| checking permissions | 0.000025 | 7.10 | 1 | 0.0000250000 |
| cleaning up | 0.000023 | 6.53 | 1 | 0.0000230000 |
| System lock | 0.000019 | 5.40 | 1 | 0.0000190000 |
| statistics | 0.000018 | 5.11 | 1 | 0.0000180000 |
| preparing | 0.000016 | 4.55 | 1 | 0.0000160000 |
| optimizing | 0.000015 | 4.26 | 1 | 0.0000150000 |
| freeing items | 0.000014 | 3.98 | 1 | 0.0000140000 |
| query end | 0.000013 | 3.69 | 1 | 0.0000130000 |
| closing tables | 0.000012 | 3.41 | 1 | 0.0000120000 |
| executing | 0.000011 | 3.13 | 1 | 0.0000110000 |
| end | 0.000011 | 3.13 | 1 | 0.0000110000 |
+----------------------+----------+-------+-------+--------------+
#通过这个结果可以很容易看到查询时间长主要是因为花了很大时间在sending data上
#这个状态 代表的原因非常多,可能是各种不同的服务器活动,包括在关联时搜索匹配的行记录等,这部分很难说能优化节省多少消耗的时间。
#若Sorting result花费的时间比较多,则可以考虑增大sort buffer size
SHOW Status
利用show status里句柄计数器,临时文件和表计数器等查看当前语句哪些操作代价高。比如创建临时表,未使用索引。
flush status;
select * from sakila.nicer_but_slower_film_list;
#...............
show status where variable_name like "Handler%" or Variable_name like "Created%";
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Created_tmp_disk_tables | 2 |
| Created_tmp_files | 2 |
| Created_tmp_tables | 3 |
| Handler_commit | 1 |
| Handler_delete | 0 |
| Handler_discover | 0 |
| Handler_external_lock | 10 |
| Handler_mrr_init | 0 |
| Handler_prepare | 0 |
| Handler_read_first | 3 |
| Handler_read_key | 12942 |
| Handler_read_last | 0 |
| Handler_read_next | 6462 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 5462 |
| Handler_read_rnd_next | 6478 |
| Handler_rollback | 0 |
| Handler_savepoint | 0 |
| Handler_savepoint_rollback | 0 |
| Handler_update | 0 |
| Handler_write | 0 |
+----------------------------+-------+
前面三个可以分析出创建了多少张临时表,注意show status本身也会创建一个临时表。通过Handler_read_rnd_next可以看出很多没有用到索引的操作。而Handler_read_key则表示用到索引的操作
最后explain执行计划查看的估计的结果,而show status则是实际测量的结果。
参考