"超級攻略:如何快速排查和優化慢SQL,提升系統速度!"
慢查詢指的是數據庫中執行時間超過指定閾值的 SQL 語句。不同業務場景下,這個閾值通常各不相同。在我們公司內部,這個閾值被設定為 1 秒鐘。也就是說,任何執行時間超過 1 秒的 SQL 語句都會被視為慢查詢。
對慢查詢進行問題排查通常分為以下幾個步驟:
發現問題
一般而言,慢查詢問題相對容易發現。如果有完善的監控體系,系統會定期統計慢 SQL 并通過報警方式提醒。
此外,如果使用了某些數據庫中間件,例如 TDDL,它們通常會記錄慢 SQL 的日志:
Cause: ERR-CODE: [TDDL-4202][ERR_SQL_QUERY_TIMEOUT] Slow query leads to a timeout exception, please contact DBA to check slow sql. SocketTimout:12000 ms,
如果只依賴 MySQL 本身的話
- 找到 MySQL 的配置文件 my.cnf(或者在 Windows 系統下可能是 my.ini),通常它們位于 MySQL 安裝目錄下的 etc 或 conf 文件夾中。
- 啟用慢查詢日志功能:請找到以下配置項,并將其取消注釋(如果已注釋),確保以下行存在或添加到配置文件中:
slow_query_log = 1
slow_query_log_file = /path/to/slow-query.log
long_query_time = 1
- 保存配置文件后,重新啟動 MySQL 服務以使配置生效。
- 查看慢查詢日志:使用文本編輯器打開慢查詢日志文件。日志文件的路徑通常在配置文件中指定。例如,在 Linux 系統上,可以使用以下命令來查看慢查詢日志文件:
sudo vi /var/log/mysql/mysql-slow.log
請將路徑 /var/log/mysql/mysql-slow.log 替換為實際配置文件中指定的慢查詢日志路徑。配置完畢后,MySQL 會將執行時間超過 long_query_time 設置的時間閾值的 SQL 語句記錄到慢查詢日志中。
如果有慢 SQL,內容如下:
# Time: 2023-06-04T12:00:00.123456Z
# User@Host: hollis[192.168.0.1]:3306
# Query_time: 2.345678 Lock_time: 0.012345 Rows_sent: 10 Rows_examined: 100
SET timestamp=1650000000;
SELECT * FROM orders WHERE status = 'pending' ORDER BY gmt_created DESC;
定位問題
在上述各種監控、報警和日志中,我們可以定位到具體的慢 SQL 語句,然后可以進一步分析為什么這個 SQL 語句執行緩慢,主要是排查以下幾個可能的原因:
- 缺少索引:沒有為查詢涉及的列創建適當的索引,導致數據庫需要全表掃描來找到匹配的行。
- 錯誤使用索引:使用了索引但不符合最左前綴原則,或者索引選擇度不高(即索引列的唯一性不夠高),導致數據庫選擇不到最優的索引執行查詢。
- 查詢字段過多:SELECT 語句中涉及的字段過多,增加了數據傳輸和處理的開銷。
- 多次回表:查詢執行過程中需要多次訪問磁盤以獲取額外的數據行,例如對主鍵的索引掃描后,需要再次根據主鍵進行查詢。
- 多表連接:涉及多個表的 JOIN 操作,若 JOIN 條件不合適或者 JOIN 操作沒有利用到索引,會導致性能下降。
- 深度分頁:需要返回大量數據中的某一頁,但是沒有合適的方式來快速定位和獲取這一頁數據。
- 其他因素:還有一些其他可能的原因,例如復雜的子查詢、數據庫服務器負載高、SQL 語句寫法不佳等。
針對這些問題,可以通過優化數據庫表結構、添加合適的索引、優化 SQL 語句寫法、調整數據庫配置參數等方式來改進 SQL 查詢的性能。
對于大多數情況下的慢 SQL 問題,通常可以通過執行計劃分析找出根本原因,主要集中在索引和 JOIN 操作上。
解決問題
定位問題后,解決問題就會變得容易起來。
實際上,最大的挑戰不在于解決問題,而在于準確定位問題。因為一旦問題被準確定位,解決起來就變得相對簡單。例如,缺少索引就添加索引,JOIN 操作過多就進行拆分。這里不再詳細展開。