"超級(jí)攻略:如何快速排查和優(yōu)化慢SQL,提升系統(tǒng)速度!"
慢查詢(xún)指的是數(shù)據(jù)庫(kù)中執(zhí)行時(shí)間超過(guò)指定閾值的 SQL 語(yǔ)句。不同業(yè)務(wù)場(chǎng)景下,這個(gè)閾值通常各不相同。在我們公司內(nèi)部,這個(gè)閾值被設(shè)定為 1 秒鐘。也就是說(shuō),任何執(zhí)行時(shí)間超過(guò) 1 秒的 SQL 語(yǔ)句都會(huì)被視為慢查詢(xún)。
對(duì)慢查詢(xún)進(jìn)行問(wèn)題排查通常分為以下幾個(gè)步驟:
發(fā)現(xiàn)問(wèn)題
一般而言,慢查詢(xún)問(wèn)題相對(duì)容易發(fā)現(xiàn)。如果有完善的監(jiān)控體系,系統(tǒng)會(huì)定期統(tǒng)計(jì)慢 SQL 并通過(guò)報(bào)警方式提醒。
此外,如果使用了某些數(shù)據(jù)庫(kù)中間件,例如 TDDL,它們通常會(huì)記錄慢 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,
如果只依賴(lài) MySQL 本身的話(huà)
- 找到 MySQL 的配置文件 my.cnf(或者在 Windows 系統(tǒng)下可能是 my.ini),通常它們位于 MySQL 安裝目錄下的 etc 或 conf 文件夾中。
- 啟用慢查詢(xún)?nèi)罩竟δ埽赫?qǐng)找到以下配置項(xiàng),并將其取消注釋?zhuān)ㄈ绻炎⑨專(zhuān)?,確保以下行存在或添加到配置文件中:
slow_query_log = 1
slow_query_log_file = /path/to/slow-query.log
long_query_time = 1
- 保存配置文件后,重新啟動(dòng) MySQL 服務(wù)以使配置生效。
- 查看慢查詢(xún)?nèi)罩荆菏褂梦谋揪庉嬈鞔蜷_(kāi)慢查詢(xún)?nèi)罩疚募?。日志文件的路徑通常在配置文件中指定。例如,?Linux 系統(tǒng)上,可以使用以下命令來(lái)查看慢查詢(xún)?nèi)罩疚募?/li>
sudo vi /var/log/mysql/mysql-slow.log
請(qǐng)將路徑 /var/log/mysql/mysql-slow.log 替換為實(shí)際配置文件中指定的慢查詢(xún)?nèi)罩韭窂?。配置完畢后,MySQL 會(huì)將執(zhí)行時(shí)間超過(guò) long_query_time 設(shè)置的時(shí)間閾值的 SQL 語(yǔ)句記錄到慢查詢(xún)?nèi)罩局小?/p>
如果有慢 SQL,內(nèi)容如下:
# 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;
定位問(wèn)題
在上述各種監(jiān)控、報(bào)警和日志中,我們可以定位到具體的慢 SQL 語(yǔ)句,然后可以進(jìn)一步分析為什么這個(gè) SQL 語(yǔ)句執(zhí)行緩慢,主要是排查以下幾個(gè)可能的原因:
- 缺少索引:沒(méi)有為查詢(xún)涉及的列創(chuàng)建適當(dāng)?shù)乃饕?,?dǎo)致數(shù)據(jù)庫(kù)需要全表掃描來(lái)找到匹配的行。
- 錯(cuò)誤使用索引:使用了索引但不符合最左前綴原則,或者索引選擇度不高(即索引列的唯一性不夠高),導(dǎo)致數(shù)據(jù)庫(kù)選擇不到最優(yōu)的索引執(zhí)行查詢(xún)。
- 查詢(xún)字段過(guò)多:SELECT 語(yǔ)句中涉及的字段過(guò)多,增加了數(shù)據(jù)傳輸和處理的開(kāi)銷(xiāo)。
- 多次回表:查詢(xún)執(zhí)行過(guò)程中需要多次訪問(wèn)磁盤(pán)以獲取額外的數(shù)據(jù)行,例如對(duì)主鍵的索引掃描后,需要再次根據(jù)主鍵進(jìn)行查詢(xún)。
- 多表連接:涉及多個(gè)表的 JOIN 操作,若 JOIN 條件不合適或者 JOIN 操作沒(méi)有利用到索引,會(huì)導(dǎo)致性能下降。
- 深度分頁(yè):需要返回大量數(shù)據(jù)中的某一頁(yè),但是沒(méi)有合適的方式來(lái)快速定位和獲取這一頁(yè)數(shù)據(jù)。
- 其他因素:還有一些其他可能的原因,例如復(fù)雜的子查詢(xún)、數(shù)據(jù)庫(kù)服務(wù)器負(fù)載高、SQL 語(yǔ)句寫(xiě)法不佳等。
針對(duì)這些問(wèn)題,可以通過(guò)優(yōu)化數(shù)據(jù)庫(kù)表結(jié)構(gòu)、添加合適的索引、優(yōu)化 SQL 語(yǔ)句寫(xiě)法、調(diào)整數(shù)據(jù)庫(kù)配置參數(shù)等方式來(lái)改進(jìn) SQL 查詢(xún)的性能。
對(duì)于大多數(shù)情況下的慢 SQL 問(wèn)題,通??梢酝ㄟ^(guò)執(zhí)行計(jì)劃分析找出根本原因,主要集中在索引和 JOIN 操作上。
解決問(wèn)題
定位問(wèn)題后,解決問(wèn)題就會(huì)變得容易起來(lái)。
實(shí)際上,最大的挑戰(zhàn)不在于解決問(wèn)題,而在于準(zhǔn)確定位問(wèn)題。因?yàn)橐坏﹩?wèn)題被準(zhǔn)確定位,解決起來(lái)就變得相對(duì)簡(jiǎn)單。例如,缺少索引就添加索引,JOIN 操作過(guò)多就進(jìn)行拆分。這里不再詳細(xì)展開(kāi)。