MySQL慢SQL優化思路
具體思路:
1、慢查詢日志記錄慢 SQL
2、explain 分析 SQL 的執行計劃
3、profile 分析執行耗時
4、Optimizer Trace 分析詳情
5、確定問題并采用相應的措施
1、查看慢日志
1.1 使用命令查詢慢日志配置
mysql> show variables like 'slow_query_log%';
+---------------------+---------------------------------------------------------------------+
| Variable_name | Value |
+---------------------+---------------------------------------------------------------------+
| slow_query_log | OFF |
| slow_query_log_file | C:\ProgramData\MySQL\MySQL Server 5.5\Data\DESKTOP-CR3IL33-slow.log |
+---------------------+---------------------------------------------------------------------+
2 rows in set (0.00 sec)
slow_query_log
:表示慢查詢開啟的狀態。slow_query_log_file
:表示慢查詢日志存放的位置。
1.2 使用命令查看超過多少時間才記錄到慢查詢日志
mysql> show variables like 'long_query_time';
+-----------------+-----------+
| Variable_name | Value |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+
1 row in set (0.00 sec)
long_query_time
:表示查詢超過多少秒才記錄到慢查詢日志。
1.3 查看是否開啟記錄未使用索引sql慢查詢日志
-- 默認是關閉的
mysql> show variables like 'log_queries_not_using_indexes';
+-------------------------------+-------+
| Variable_name | Value |
+-------------------------------+-------+
| log_queries_not_using_indexes | OFF |
+-------------------------------+-------+
1 row in set (0.00 sec)
1.4 啟用慢日志
-- 啟用慢查詢,加上global,不然會報錯的
mysql> set global slow_query_log='ON';
Query OK, 0 rows affected (0.01 sec)
-- 是否開啟慢查詢
mysql> show variables like 'slow_query_log%';
+---------------------+---------------------------------------------------------------------+
| Variable_name | Value |
+---------------------+---------------------------------------------------------------------+
| slow_query_log | ON |
| slow_query_log_file | C:\ProgramData\MySQL\MySQL Server 5.5\Data\DESKTOP-CR3IL33-slow.log |
+---------------------+---------------------------------------------------------------------+
2 rows in set (0.00 sec)
1.5 修改慢查詢時間
-- 修改慢查詢時間
mysql> set global long_query_time=2;
Query OK, 0 rows affected (0.00 sec)
-- 查看慢查詢時間閾值
mysql> show variables like 'long_query_time';
+-----------------+----------+
| Variable_name | Value |
+-----------------+----------+
| long_query_time | 2.000000 |
+-----------------+----------+
1 row in set (0.00 sec)-- 修改了慢查詢日志馬上用上述命令查看發現不生效,其實已經修改成功,可以用下面的命令
mysql> show global variables like 'long_query_time';
+-----------------+----------+
| Variable_name | Value |
+-----------------+----------+
| long_query_time | 2.000000 |
+-----------------+----------+
1 row in set (0.00 sec)
1.6 開啟記錄未使用索引sql慢查詢日志
-- 開啟
mysql> set global log_queries_not_using_indexes=1;
Query OK, 0 rows affected (0.00 sec)
-- 查詢
mysql> show variables like 'log_queries_not_using_indexes';
+-------------------------------+-------+
| Variable_name | Value |
+-------------------------------+-------+
| log_queries_not_using_indexes | ON |
+-------------------------------+-------+
1 row in set (0.00 sec)
1.7 永久生效
上面的操作并不是永久開啟慢查詢日志,如果要永久生效,就必須修改配置文件 my.cnf
或 my.ini
,在該文件
中,找到或在 [mysqld]
部分下添加以下內容,然后保存文件并重啟 MySQL 服務。
# 開啟慢查詢日志
slow_query_log=1# 指定慢查詢日志生成文件所在路徑
slow_query_log_file=D:\OwnerSoftwareInstall\MySQL\zsx-slow.log# 設置慢查詢時間閾值
long_query_time=2# 開啟記錄未使用索引sql慢查詢日志
log_queries_not_using_indexes=1# 日志輸出方式為文件
log_output=FILE
-- 查看
mysql> show variables like 'slow_query_log%';
+---------------------+--------------------------------------------+
| Variable_name | Value |
+---------------------+--------------------------------------------+
| slow_query_log | ON |
| slow_query_log_file | D:\OwnerSoftwareInstall\MySQL\zsx-slow.log |
+---------------------+--------------------------------------------+
2 rows in set (0.00 sec)
1.8 定位慢查詢
看看mysql從啟動到現在,mysql數據庫的一些運行狀態:
mysql> flush status;
Query OK, 0 rows affected (0.00 sec)
-- 從啟動到現在執行select次數
mysql> show global status like 'com_select';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_select | 3 |
+---------------+-------+
1 row in set (0.00 sec)
-- 當前session里執行select次數,session可以不加
mysql> show session status like 'com_select';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_select | 1 |
+---------------+-------+
1 row in set (0.00 sec)
-- 從啟動到現在執行update次數
mysql> show global status like 'com_update';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_update | 0 |
+---------------+-------+
1 row in set (0.00 sec)
-- 當前session里執行update次數,session可以不加
mysql> show session status like 'com_update';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_update | 0 |
+---------------+-------+
1 row in set (0.00 sec)
-- 從啟動到現在執行delete次數
mysql> show global status like 'com_delete';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_delete | 0 |
+---------------+-------+
1 row in set (0.00 sec)
-- 當前session里執行delete次數,session可以不加
mysql> show session status like 'com_delete';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_delete | 0 |
+---------------+-------+
1 row in set (0.00 sec)
-- 從開啟慢查詢日志開始到現在有多少條慢查詢記錄
mysql> show global status like '%slow_queries%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Slow_queries | 1 |
+---------------+-------+
1 row in set (0.00 sec)
1.9 使用mysqldumpslow工具分析慢查詢
日志分析工具mysqldumpslow,mysql官方自帶的,只要安裝了mysql就可以使用它,可以用來幫助我們分析慢
日志文件。
# 指定慢查詢日志進行分析
# 查詢執行時間最長的前10
$ perl mysqldumpslow.pl -s t -a -t 10 D:\OwnerSoftwareInstall\MySQL\zsx-slow.log
$ perl mysqldumpslow.pl --help
Locale 'Chinese (Simplified)_China.936' is unsupported, and may crash the interpreter.
Usage: mysqldumpslow [ OPTS... ] [ LOGS... ]Parse and summarize the MySQL slow query log. Options are--verbose verbose--debug debug--help write this text to standard output-v verbose-d debug-s ORDER what to sort by (al, at, ar, c, l, r, t), 'at' is defaultal: average lock timear: average rows sentat: average query timec: countl: lock timer: rows sentt: query time-r reverse the sort order (largest last instead of first)-t NUM just show the top n queries-a don't abstract all numbers to N and strings to 'S'-n NUM abstract numbers with at least n digits within names-g PATTERN grep: only consider stmts that include this string-h HOSTNAME hostname of db server for *-slow.log filename (can be wildcard),default is '*', i.e. match all-i NAME name of server instance (if using mysql.server startup script)-l don't subtract lock time from total time
參數選項 | 使用說明 |
---|---|
-a | 顯示具體的數字和字符信息,而不是用N或者S代替 |
-n | 將數字抽象顯示為指定的數字個數 |
–debug | -d | 指定debug模式運行 |
-g | 指定大小寫不敏感的正則表達 |
–help | 顯示幫助信息 |
-h | 指定MySQL主機名稱用于選擇慢查詢日志 |
-i | 指定服務器示例名稱用于選擇慢查詢日志 |
-l | 顯示總時間(包括lock鎖定時間) |
-r | 逆序排序 |
-s | 指定排序方式 |
-t | 只顯示指定數量的結果內容 |
–verbose | -v | Verbose模式 |
使用-s指定排序方式,主要有如下四種方式:
- l: 按鎖定時間排序
- r: 按結果行數排序
- t: 按查詢時間排序
- c:按執行次數排序
a為平均,與上述參數結合可形成新的排序方式:
- at:按平均查詢時間排序(默認排序方式)
- al:按平均鎖定時間排序
- ar:按平均結果行數排序
# 返回執行次數最高的前10條sql
$ perl mysqldumpslow.pl -s c -a -t 10 D:\OwnerSoftwareInstall\MySQL\zsx-slow.log# 返回結果行數最多的前10條sql
$ perl mysqldumpslow.pl -s r -a -t 10 D:\OwnerSoftwareInstall\MySQL\zsx-slow.log# 返回執行時間最長的前10條sql
$ perl mysqldumpslow.pl -s t -a -t 10 D:\OwnerSoftwareInstall\MySQL\zsx-slow.log
分析后結果參數解讀
- Count:代表這個 SQL 語句執行了多少次
- Time:代表執行的時間,括號是累計時間
- Lock:表示鎖定的時間,括號是累計時間
- Rows:表示返回的記錄數,括號是累計記錄數
2、explain 分析 SQL 的執行計劃
mysql> explain select * from student;
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | student | ALL | NULL | NULL | NULL | NULL | 7 | |
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
1 row in set (0.00 sec)
type=all 代表進行了全表掃描。
3、show profile分析
了解SQL執行的線程的狀態及消耗的時間。
profiling 默認是關閉,我們可以使用命令查看是否開啟:
-- 未開啟
mysql> show variables like '%profil%';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| have_profiling | YES |
| profiling | OFF |
| profiling_history_size | 15 |
+------------------------+-------+
3 rows in set (0.00 sec)-- 開啟
mysql> show variables like '%profil%';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| have_profiling | YES |
| profiling | ON |
| profiling_history_size | 15 |
+------------------------+-------+
3 rows in set (0.00 sec)
-- 默認是關閉的,開啟語句set profiling = 1;也可以使用set profiling=ON;開啟
mysql> set profiling =1;
Query OK, 0 rows affected (0.00 sec)-- 執行SQL
mysql> select * from student;
+----+-------+
| id | name |
+----+-------+
| 1 | John |
| 2 | tom |
| 3 | marry |
| 6 | marry |
| 7 | tom |
| 10 | marry |
| 11 | tom |
+----+-------+
7 rows in set (0.00 sec)-- 可以看到實際的執行語句
mysql> show profiles;
+----------+------------+-----------------------+
| Query_ID | Duration | Query |
+----------+------------+-----------------------+
| 1 | 0.00017550 | select * from student |
+----------+------------+-----------------------+
1 row in set (0.00 sec)
show profiles
會顯示最近發給服務器的多條語句,條數由變量 profiling_history_size
定義,默認是15。
如果我們需要看單獨某條SQL的分析,可以show profile
查看最近一條SQL的分析,也可以使用show profile
for query id
(其中id就是show profiles中的QUERY_ID)查看具體一條的SQL語句分析。
mysql> show profiles;
+----------+------------+-----------------------+
| Query_ID | Duration | Query |
+----------+------------+-----------------------+
| 1 | 0.00017550 | select * from student |
+----------+------------+-----------------------+
1 row in set (0.00 sec)
mysql> show profile for query 1;
+----------------------+----------+
| Status | Duration |
+----------------------+----------+
| starting | 0.000024 |
| checking permissions | 0.000003 |
| Opening tables | 0.000015 |
| System lock | 0.000004 |
| init | 0.000008 |
| optimizing | 0.000002 |
| statistics | 0.000005 |
| preparing | 0.000004 |
| executing | 0.000001 |
| Sending data | 0.000033 |
| end | 0.000002 |
| query end | 0.000002 |
| closing tables | 0.000003 |
| freeing items | 0.000025 |
| logging slow query | 0.000001 |
| logging slow query | 0.000044 |
| cleaning up | 0.000002 |
+----------------------+----------+
17 rows in set (0.00 sec)
除了查看 profile ,還可以查看 cpu 和 io:
mysql> show profile cpu,block io for query 1;
+----------------------+----------+----------+------------+--------------+---------------+
| Status | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out |
+----------------------+----------+----------+------------+--------------+---------------+
| starting | 0.000024 | 0.000000 | 0.000000 | NULL | NULL |
| checking permissions | 0.000003 | 0.000000 | 0.000000 | NULL | NULL |
| Opening tables | 0.000015 | 0.000000 | 0.000000 | NULL | NULL |
| System lock | 0.000004 | 0.000000 | 0.000000 | NULL | NULL |
| init | 0.000008 | 0.000000 | 0.000000 | NULL | NULL |
| optimizing | 0.000002 | 0.000000 | 0.000000 | NULL | NULL |
| statistics | 0.000005 | 0.000000 | 0.000000 | NULL | NULL |
| preparing | 0.000004 | 0.000000 | 0.000000 | NULL | NULL |
| executing | 0.000001 | 0.000000 | 0.000000 | NULL | NULL |
| Sending data | 0.000033 | 0.000000 | 0.000000 | NULL | NULL |
| end | 0.000002 | 0.000000 | 0.000000 | NULL | NULL |
| query end | 0.000002 | 0.000000 | 0.000000 | NULL | NULL |
| closing tables | 0.000003 | 0.000000 | 0.000000 | NULL | NULL |
| freeing items | 0.000025 | 0.000000 | 0.000000 | NULL | NULL |
| logging slow query | 0.000001 | 0.000000 | 0.000000 | NULL | NULL |
| logging slow query | 0.000044 | 0.000000 | 0.000000 | NULL | NULL |
| cleaning up | 0.000002 | 0.000000 | 0.000000 | NULL | NULL |
+----------------------+----------+----------+------------+--------------+---------------+
17 rows in set (0.00 sec)
4、Optimizer Trace分析詳情(mysql 5.6 及以上版本)
profile 只能查看到 SQL 的執行耗時,但是無法看到 SQL 真正執行的過程信息,即不知道 MySQL 優化器是如何選
擇執行計劃。這時候,我們可以使用Optimizer Trace
,它可以跟蹤執行語句的解析優化執行的全過程。
-- 第一步打開trace,設置格式為JSON格式
mysql> set session optimizer_trace="enabled=on",end_markers_in_json=ON;
Query OK, 0 rows affected (0.00 sec)-- 設置優化器跟蹤使用的最大內存量
mysql> set session optimizer_trace_max_mem_size=1000000;
Query OK, 0 rows affected (0.00 sec)-- 查看
mysql> show session variables like 'optimizer_trace';
+-----------------+-------------------------+
| Variable_name | Value |
+-----------------+-------------------------+
| optimizer_trace | enabled=on,one_line=off |
+-----------------+-------------------------+
1 row in set (0.00 sec)
-- 第二步,執行要分析的sql語句
mysql> select * from student;
+----+-------+
| id | name |
+----+-------+
| 1 | John |
| 2 | tom |
| 3 | marry |
| 6 | marry |
| 7 | tom |
| 10 | marry |
| 11 | tom |
+----+-------+
7 rows in set (0.00 sec)
-- 第三步,查看information_schema.OPTIMIZER_TRACE,查看sql語句執行跟蹤記錄
mysql> select * from information_schema.optimizer_trace;
+-----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------+-------------------------+
| QUERY | TRACE | MISSING_BYTES_BEYOND_MAX_MEM_SIZE | INSUFFICIENT_PRIVILEGES |
+-----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------+-------------------------+
| select * from student | {"steps": [{"join_preparation": {"select#": 1,"steps": [{"expanded_query": "/* select#1 */ select `student`.`id` AS `id`,`student`.`name` AS `name` from `student`"}] /* steps */} /* join_preparation */},{"join_optimization": {"select#": 1,"steps": [{"table_dependencies": [{"table": "`student`","row_may_be_null": false,"map_bit": 0,"depends_on_map_bits": [] /* depends_on_map_bits */}] /* table_dependencies */},{"rows_estimation": [{"table": "`student`","table_scan": {"rows": 7,"cost": 0.25} /* table_scan */}] /* rows_estimation */},{"considered_execution_plans": [{"plan_prefix": [] /* plan_prefix */,"table": "`student`","best_access_path": {"considered_access_paths": [{"rows_to_scan": 7,"access_type": "scan","resulting_rows": 7,"cost": 0.95,"chosen": true}] /* considered_access_paths */} /* best_access_path */,"condition_filtering_pct": 100,"rows_for_plan": 7,"cost_for_plan": 0.95,"chosen": true}] /* considered_execution_plans */},{"attaching_conditions_to_tables": {"original_condition": null,"attached_conditions_computation": [] /* attached_conditions_computation */,"attached_conditions_summary": [{"table": "`student`","attached": null}] /* attached_conditions_summary */} /* attaching_conditions_to_tables */},{"finalizing_table_conditions": [] /* finalizing_table_conditions */},{"refine_plan": [{"table": "`student`"}] /* refine_plan */}] /* steps */} /* join_optimization */},{"join_execution": {"select#": 1,"steps": [] /* steps */} /* join_execution */}] /* steps */
} | 0 | 0 |
+-----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------+-------------------------+
1 row in set (0.00 sec)
-- 第四步,關閉trace。
mysql> set session optimizer_trace="enabled=off";
Query OK, 0 rows affected (0.00 sec)-- 查看
mysql> show session variables like 'optimizer_trace';
+-----------------+--------------------------+
| Variable_name | Value |
+-----------------+--------------------------+
| optimizer_trace | enabled=off,one_line=off |
+-----------------+--------------------------+
1 row in set (0.00 sec)
可以查看分析其執行樹,會包括三個階段:
- join_preparation:準備階段
- join_optimization:分析階段
- join_execution:執行階段
5、確定問題并采用相應的措施
根據上面4個步驟的分析結果進行相應的優化。