十四、SQL優化工具
1 慢查詢日志
MySQL的慢查詢日志是MySQL提供的一種日志記錄,它用來記錄在MySQL中響應時間超過閥值的語句,具體指運行時間超過long_query_time值的SQL,則會被記錄到慢查詢日志中。long_query_time的默認值為10,運行10秒以上的SQL語句會被記錄下來。由慢查詢日志來查看哪些SQL超出了我們的最大忍耐時間值,比如一條sql執行超過5秒鍾,我們就算慢SQL,希望能收集超過5秒的sql,結合explain進行全面分析。
1.1 開啟慢查詢日志
默認情況下,MySQL數據庫沒有開啟慢查詢日志,需要我們手動來設置這個參數。如果不是調優需要,一般不建議開啟該參數,因為開啟慢查詢日志或多或少會帶來一定的性能影響。
首先查看mysql的慢查詢日志是否開啟,通過如下命令:
SHOW VARIABLES LIKE '%slow_query_log%';
slow_query_log
為OFF
表示關閉了慢查詢日志,slow_query_log_file
表示日志的存放位置。
使用如下命令開啟慢查詢日志:
SET GLOBAL slow_query_log = 1;
使用此方法開啟了慢查詢日志只對當前數據庫生效,如果MySQL重啟后則會失效。如果要永久生效,就必須修改配置文件my.cnf
。修改如下位置即可:
[mysqld]
slow_query_log =1
slow_query_log_file=/var/lib/mysql/Heygo-slow.log
1.2 修改參數
開啟慢查詢日志后,什么樣的SQL會記錄到慢查詢里面?這個是由參數long_query_time
控制,默認情況下long_query_time的值為10秒,使用如下命令查看慢SQL記錄的閾值:
SHOW VARIABLES LIKE 'long_query_time%';
另外,運行時間正好等於long_query_time
並不會被記錄下來。
可以手動修改這個閾值,通過命令臨時修改(永久修改需要修改my.cnf
):
SET GLOBAL long_query_time = 5;
執行之后需要重新連接或者新開一個客戶端連接才能看到修改值。
1.3 查看日志
現在,執行如下語句,一定會被慢日志記錄:
SELECT sleep(6);
慢查詢日志文件默認在/var/lib/mysql/
下,后綴為-slow.log
,查看一下:
cat 68926f356828-slow.log
mysqld, Version: 5.7.37 (MySQL Community Server (GPL)). started with:
Tcp port: 3306 Unix socket: /var/run/mysqld/mysqld.sock
Time Id Command Argument
# Time: 2022-01-28T10:57:27.035023Z
# User@Host: root[root] @ [192.168.142.3] Id: 7
# Query_time: 6.000578 Lock_time: 0.000000 Rows_sent: 1 Rows_examined: 0
SET timestamp=1643367447;
SELECT sleep(6);
查詢當前系統中有多少條慢查詢記錄:
SHOW GLOBAL STATUS LIKE '%Slow_queries%';
1.4 日志分析命令
在生產環境中,如果要手工分析日志,查找、分析SQL,顯然是個體力活,MySQL提供了日志分析工具mysqldumpslow
。
# 查看幫助:mysqldumpslow --help
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 default # 表示按何種方式排序 默認為at
al: average lock time # 按平均鎖定時間
ar: average rows sent # 按平均返回記錄數
at: average query time # 按平均查詢時間
c: count # 按訪問次數
l: lock time # 按鎖定時間
r: rows sent # 按返回記錄
t: query time # 按查詢時間
-r reverse the sort order (largest last instead of first)
-t NUM just show the top n queries # 只顯示前N條查詢
-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
常見使用示例:
# 得到返回記錄集最多的10個SQL
mysqldumpslow -s r -t 10 /var/lib/mysql/68926f356828-slow.log
# 得到訪問次數最多的10個SQL
mysqldumpslow -s c -t 10 /var/lib/mysql/68926f356828-slow.log
# 得到按照時間排序的前10條里面含有左連接的查詢語句
mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/68926f356828-slow.log
# 在使用這些命令時結合 | 和more使用,因為如果日志很大,可能會刷屏
mysqldumpslow -s r -t 10 /var/lib/mysql/68926f356828-slow.log | more
2 Show Profile
Show Profile是mysql提供可以用來分析當前會話中語句執行的資源消耗情況,可以用於SQL的調優測量。默認情況下,參數處於關閉狀態,並保存最近15次的運行結果。
2.1 開啟Show Profile
查看Show Profile是否開啟,使用如下命令:
SHOW VARIABLES LIKE 'profiling%';
開啟Show Profile:
SET profiling = ON;
2.2 使用Show Profile
下面運行幾條SQL嘗試一下:
# 普通sql
select * from tbl_emp;
select * from tbl_emp e inner join tbl_dept d on e.deptId = d.id;
select * from tbl_emp e left join tbl_dept d on e.deptId = d.id;
# 慢sql
select * from emp group by id%10 limit 150000;
select * from emp group by id%10 limit 150000;
select * from emp group by id%20 order by 5;
查看結果:
mysql> SHOW PROFILES;
+----------+------------+----------------------------------------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+----------------------------------------------------------------------+
| 1 | 0.00052700 | show variables like 'profiling%' |
| 2 | 0.00030300 | select * from tbl_emp |
| 3 | 0.00010650 | select * from tbl_emp e inner join tbl_dept d on e.'deptId' = d.'id' |
| 4 | 0.00031625 | select * from tbl_emp e inner join tbl_dept d on e.deptId = d.id |
| 5 | 0.00042100 | select * from tbl_emp e left join tbl_dept d on e.deptId = d.id |
| 6 | 0.38621875 | select * from emp group by id%20 limit 150000 |
| 7 | 0.00014900 | select * from emp group by id%20 order by 150000 |
| 8 | 0.38649000 | select * from emp group by id%20 order by 5 |
| 9 | 0.06782700 | select COUNT(*) from emp |
| 10 | 0.35434400 | select * from emp group by id%10 limit 150000 |
+----------+------------+----------------------------------------------------------------------+
2.3 診斷SQL
查看SQL語句執行的具體流程以及每個步驟花費的時間:
SHOW PROFILE cpu,block io FOR QUERY SQL編號;
# 查詢參數解釋:
# ALL:顯示所有的開銷信息
# BLOCK IO:顯示塊IO相關開銷
# CONTEXT SWITCHES:上下文切換相關開銷
# CPU:顯示CPU相關開銷信息
# IPC:顯示發送和接收相關開銷信息
# MEMORY:顯示內存相關開銷信息
# PAGE FAULTS:顯示頁面錯誤相關開銷信息
# SOURCE:顯示和Source_function,Source_file,Source_line相關的開銷信息
# SWAPS:顯示交換次數相關開銷的信息
示例:
mysql> SHOW PROFILE cpu,block io FOR QUERY 2;
+----------------------+----------+----------+------------+--------------+---------------+
| Status | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out |
+----------------------+----------+----------+------------+--------------+---------------+
| starting | 0.000055 | 0.000000 | 0.000000 | 0 | 0 |
| checking permissions | 0.000007 | 0.000000 | 0.000000 | 0 | 0 |
| Opening tables | 0.000011 | 0.000000 | 0.000000 | 0 | 0 |
| init | 0.000024 | 0.000000 | 0.000000 | 0 | 0 |
| System lock | 0.000046 | 0.000000 | 0.000000 | 0 | 0 |
| optimizing | 0.000018 | 0.000000 | 0.000000 | 0 | 0 |
| statistics | 0.000008 | 0.000000 | 0.000000 | 0 | 0 |
| preparing | 0.000019 | 0.000000 | 0.000000 | 0 | 0 |
| executing | 0.000003 | 0.000000 | 0.000000 | 0 | 0 |
| Sending data | 0.000089 | 0.000000 | 0.000000 | 0 | 0 |
| end | 0.000004 | 0.000000 | 0.000000 | 0 | 0 |
| query end | 0.000003 | 0.000000 | 0.000000 | 0 | 0 |
| closing tables | 0.000005 | 0.000000 | 0.000000 | 0 | 0 |
| freeing items | 0.000006 | 0.000000 | 0.000000 | 0 | 0 |
| cleaning up | 0.000006 | 0.000000 | 0.000000 | 0 | 0 |
+----------------------+----------+----------+------------+--------------+---------------+
如果你使用Navicat軟件,也可以在
剖析
中查看到這些數據。
如果status狀態出現以下幾種,需要警惕:
- converting HEAP to MyISAM:查詢結果太大,內存都不夠用了往磁盤上搬了。
- Creating tmp table:創建臨時表,mysql 先將拷貝數據到臨時表,然后用完再將臨時表刪除
- Copying to tmp table on disk:把內存中臨時表復制到磁盤
- locked:鎖表
3 SQL優化思路
配合慢查詢日志和Show Profile,梳理一下SQL的優化思路。當整個sql server變慢之后,可以按照如下方法進行分析:
-
開啟慢查詢,捕獲慢sql
-
使用explain對慢查詢日志中的sql進行分析
-
使用show profile查詢sql在mysql服務器里面的執行細節和生命周期情況
-
對sql數據庫服務器的參數進行調優