MySQL慢查詢(xún)?nèi)罩鹃_(kāi)啟與使用:找到那些偷偷變慢的SQL
慢查詢(xún)?nèi)罩臼鞘裁?/h2>
慢查詢(xún)?nèi)罩臼?MySQL 提供的一個(gè)功能,它會(huì)把執(zhí)行時(shí)間超過(guò)指定閾值的 SQL 語(yǔ)句記錄到文件里。
有了慢查詢(xún)?nèi)罩?,你就不用一行一行翻代碼去猜哪條 SQL查詢(xún)較慢可能存在問(wèn)題,直接看日志就可以找到問(wèn)題SQL。
MySQL 默認(rèn)是關(guān)閉慢查詢(xún)?nèi)罩镜?,因?yàn)橛涗浫罩颈旧碛行阅荛_(kāi)銷(xiāo),所以生產(chǎn)環(huán)境通常只在排查問(wèn)題時(shí)臨時(shí)開(kāi)啟,或者設(shè)置一個(gè)相對(duì)嚴(yán)格的閾值(比如 1 秒),只記錄真正有問(wèn)題的 SQL。
開(kāi)啟慢查詢(xún)?nèi)罩?/h2>
查看當(dāng)前狀態(tài)
-- 查看慢查詢(xún)?nèi)罩臼欠耖_(kāi)啟 SHOW VARIABLES LIKE 'slow_query_log'; -- 查看閾值(單位:秒) SHOW VARIABLES LIKE 'long_query_time'; -- 查看日志文件路徑 SHOW VARIABLES LIKE 'slow_query_log_file';
正常情況下,slow_query_log 的值是 OFF,long_query_time 默認(rèn)是 10 秒。10 秒太寬松了,生產(chǎn)環(huán)境一般設(shè)成 1 秒甚至 0.5 秒,主要還是取決于業(yè)務(wù)場(chǎng)景。
臨時(shí)開(kāi)啟(當(dāng)前會(huì)話(huà)生效)
-- 開(kāi)啟慢查詢(xún)?nèi)罩? SET GLOBAL slow_query_log = 'ON'; -- 設(shè)置閾值為 1 秒(超過(guò) 1 秒的 SQL 才記錄) SET GLOBAL long_query_time = 1; -- 可選:記錄沒(méi)有使用索引的 SQL SET GLOBAL log_queries_not_using_indexes = 'ON';
用 SET GLOBAL 設(shè)置的參數(shù)在 MySQL 重啟后會(huì)失效,下次啟動(dòng)還是用配置文件里的值。
永久開(kāi)啟(修改配置文件)
編輯 MySQL 的配置文件 my.cnf(Linux)或 my.ini(Windows),在 [mysqld] 段落下添加:
[mysqld] # 開(kāi)啟慢查詢(xún)?nèi)罩? slow_query_log = 1 # 慢查詢(xún)閾值:1 秒 long_query_time = 1 # 日志文件路徑(可以自定義) slow_query_log_file = /var/log/mysql/slow.log # 可選:記錄沒(méi)有使用索引的 SQL log_queries_not_using_indexes = 1
修改之后重啟 MySQL,或者執(zhí)行 SET GLOBAL 命令讓配置立即生效。
一個(gè)小技巧:log_queries_not_using_indexes 這個(gè)參數(shù)很實(shí)用。它會(huì)把所有沒(méi)走索引的 SQL 都記錄下來(lái),不管執(zhí)行時(shí)間多長(zhǎng)。這類(lèi) SQL 在數(shù)據(jù)量小的時(shí)候可能跑得很快,但隨著數(shù)據(jù)增長(zhǎng)會(huì)越來(lái)越慢,屬于"定時(shí)炸彈",有了這個(gè)功能就可以早點(diǎn)發(fā)現(xiàn)早點(diǎn)處理。
慢查詢(xún)?nèi)罩鹃L(zhǎng)什么樣
開(kāi)啟之后,我們來(lái)制造一條慢查詢(xún),看看日志長(zhǎng)什么樣。
先準(zhǔn)備一張測(cè)試表和一些數(shù)據(jù):
CREATE TABLE user_order (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
order_no VARCHAR(32) NOT NULL,
amount DECIMAL(10, 2),
status VARCHAR(20),
create_time DATETIME,
INDEX idx_user_id (user_id)
) ENGINE=InnoDB;
-- 插入 100 萬(wàn)條測(cè)試數(shù)據(jù)
DELIMITER //
CREATE PROCEDURE generate_orders(IN n INT)
BEGIN
DECLARE i INT DEFAULT 0;
WHILE i < n DO
INSERT INTO user_order (user_id, order_no, amount, status, create_time)
VALUES (
FLOOR(RAND() * 10000),
CONCAT('ORD', LPAD(i, 10, '0')),
ROUND(RAND() * 1000, 2),
ELT(FLOOR(RAND() * 3) + 1, 'pending', 'paid', 'cancelled'),
DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 365) DAY)
);
SET i = i + 1;
END WHILE;
END //
DELIMITER ;
CALL generate_orders(1000000);
現(xiàn)在執(zhí)行一條故意不走索引的查詢(xún):
-- 對(duì) status 做模糊查詢(xún),status 上沒(méi)有索引 SELECT * FROM user_order WHERE status LIKE 'p%' ORDER BY create_time DESC LIMIT 50;
等它跑完(可能要幾秒),然后去日志文件里找找看。日志文件的路徑可以用 SHOW VARIABLES LIKE 'slow_query_log_file' 查看。
一條典型的慢查詢(xún)?nèi)罩鹃L(zhǎng)這樣:
# Time: 2026-06-21T14:32:05.123456+08:00 # User@Host: root[root] @ localhost [] Id: 42 # Query_time: 2.356789 Lock_time: 0.000123 Rows_sent: 50 Rows_examined: 1000000 SET timestamp=1718946725; SELECT * FROM user_order WHERE status LIKE 'p%' ORDER BY create_time DESC LIMIT 50;
逐行解讀:
| 字段 | 含義 | 重點(diǎn)關(guān)注 |
|---|---|---|
| Time | SQL 執(zhí)行的時(shí)間點(diǎn) | 定位問(wèn)題發(fā)生的時(shí)刻 |
| User@Host | 執(zhí)行 SQL 的用戶(hù)和來(lái)源 | 排查是不是某個(gè)服務(wù)在搞事 |
| Query_time | SQL 執(zhí)行總耗時(shí)(秒) | 核心指標(biāo),越小越好 |
| Lock_time | 等待鎖的時(shí)間(秒) | 如果很大,說(shuō)明有鎖競(jìng)爭(zhēng) |
| Rows_sent | 返回給客戶(hù)端的行數(shù) | 和 LIMIT 對(duì)比,看有沒(méi)有多返回 |
| Rows_examined | 掃描的行數(shù) | 核心指標(biāo),越大說(shuō)明越低效 |
| SQL 語(yǔ)句 | 實(shí)際執(zhí)行的 SQL | 拿去 EXPLAIN 分析 |
最該關(guān)注的兩個(gè)數(shù)字:Query_time 和 Rows_examined。
Query_time 告訴你這條 SQL 到底慢不慢,Rows_examined 告訴你它為什么慢。如果 Rows_examined 是 100 萬(wàn),但 Rows_sent 只有 50,說(shuō)明 MySQL 掃了 100 萬(wàn)行才挑出 50 條——這就是典型的索引缺失或索引失效。
mysqldumpslow:日志分析工具
日志文件看幾條還行,但如果慢查詢(xún)很多,一條條翻就太低效了。MySQL 自帶了一個(gè)日志分析工具 mysqldumpslow,可以幫我們做匯總統(tǒng)計(jì)。
# 按執(zhí)行時(shí)間排序,取前 10 條最慢的 mysqldumpslow -s t -t 10 /var/log/mysql/slow.log # 按掃描行數(shù)排序,取前 10 條 mysqldumpslow -s r -t 10 /var/log/mysql/slow.log # 按執(zhí)行次數(shù)排序,取前 10 條 mysqldumpslow -s c -t 10 /var/log/mysql/slow.log
參數(shù)說(shuō)明:
| 參數(shù) | 含義 |
|---|---|
-s t | 按總執(zhí)行時(shí)間排序(默認(rèn)) |
-s r | 按掃描總行數(shù)排序 |
-s c | 按執(zhí)行次數(shù)排序 |
-s l | 按鎖等待時(shí)間排序 |
-t N | 只顯示前 N 條 |
-g "pattern" | 只匹配包含指定字符串的 SQL |
輸出結(jié)果類(lèi)似:
Count: 125 Time=2.36s (295s) Lock=0.00s (0.15s) Rows=50.0 (6250), root[root]@localhost
SELECT * FROM user_order WHERE status LIKE 'S' ORDER BY create_time DESC LIMIT N
這一行告訴我們:這條 SQL 在統(tǒng)計(jì)時(shí)段內(nèi)執(zhí)行了 125 次,平均耗時(shí) 2.36 秒,累計(jì)耗時(shí) 295 秒,平均掃描行數(shù) 6250 條。
Count 大的說(shuō)明是高頻 SQL,Time 大的說(shuō)明是耗時(shí)大戶(hù)。 如果一條 SQL Count 和 Time 都大,那它就是性能優(yōu)化的第一優(yōu)先級(jí)。
配合 EXPLAIN 做深度分析
慢查詢(xún)?nèi)罩編湍阏业搅?quot;嫌疑人",但要定罪還需要充分的證據(jù)。所以在拿到日志里的 SQL 后,我們繼續(xù)用 EXPLAIN 看看它的執(zhí)行計(jì)劃:
EXPLAIN SELECT * FROM user_order WHERE status LIKE 'p%' ORDER BY create_time DESC LIMIT 50;
輸出可能是:
+----+------+-------+------+---------+------+----------+-----------------------------+
| id | type | key | ref | rows | filtered | Extra |
+----+------+-------+------+---------+----------+------------------------------------+
| 1 | ALL | NULL | NULL | 1000000 | 33.33 | Using where; Using filesort |
+----+------+-------+------+---------+----------+------------------------------------+
四個(gè)危險(xiǎn)信號(hào):
- type = ALL:全表掃描,沒(méi)走索引
- key = NULL:沒(méi)有可用索引
- rows = 1000000:掃描了 100 萬(wàn)行
- Extra = Using filesort:額外排序
和日志里的 Rows_examined = 1000000 對(duì)上了。問(wèn)題很明確:status 列沒(méi)有索引,MySQL 只能全表掃描。
優(yōu)化方案:給 status 加索引,或者改成聯(lián)合索引 (status, create_time) 同時(shí)覆蓋過(guò)濾和排序:
ALTER TABLE user_order ADD INDEX idx_status_time (status, create_time);
再查一次 EXPLAIN:
+----+-------+----------------+------+---------+------+----------+-------+
| id | type | key | ref | rows | filtered | Extra |
+----+-------+----------------+------+---------+----------+-------------+
| 1 | range | idx_status_time| NULL | 333333 | 100.00 | Using where |
+----+-------+----------------+------+---------+----------+-------------+
type 從 ALL 變成了 range,rows 從 100 萬(wàn)降到了 33 萬(wàn),Using filesort 也沒(méi)了。執(zhí)行時(shí)間從 2 秒多降到幾百毫秒。
慢查詢(xún)?nèi)罩矩?fù)責(zé)"發(fā)現(xiàn)問(wèn)題",EXPLAIN 負(fù)責(zé)"定位原因",兩者配合才是完整的調(diào)優(yōu)鏈路。
常見(jiàn)問(wèn)題排查清單
拿到慢查詢(xún)?nèi)罩竞?,按照這個(gè)清單逐項(xiàng)檢查:
| 現(xiàn)象 | 可能原因 | 排查方法 |
|---|---|---|
| Rows_examined 遠(yuǎn)大于 Rows_sent | 索引缺失或索引失效 | EXPLAIN 看 type 和 key |
| Query_time 大但 Rows_examined 不大 | 鎖等待、IO 等待 | 關(guān)注 Lock_time,檢查是否有表鎖 |
| 同一條 SQL 時(shí)快時(shí)慢 | 執(zhí)行計(jì)劃不穩(wěn)定 | EXPLAIN 看是否有多個(gè)候選索引 |
| 日志里出現(xiàn)大量相同 SQL | 慢查詢(xún)集中在某幾張表 | mysqldumpslow -s c 統(tǒng)計(jì)頻次 |
| 某個(gè)時(shí)間段集中出現(xiàn)慢查詢(xún) | 定時(shí)任務(wù)、批量導(dǎo)入 | 檢查 Time 字段的時(shí)間分布 |
| Lock_time 很大 | 有事務(wù)長(zhǎng)時(shí)間未提交 | 檢查 SHOW ENGINE INNODB STATUS |
一個(gè)實(shí)用的排查流程:

小結(jié)
慢查詢(xún)?nèi)罩臼?MySQL 性能排查的起點(diǎn)。它幫你從海量 SQL 中篩選出真正有問(wèn)題的那些,附上執(zhí)行時(shí)間、掃描行數(shù)等關(guān)鍵指標(biāo),讓你的調(diào)優(yōu)工作有的放矢。
從實(shí)際操作來(lái)看,慢查詢(xún)?nèi)罩?+ EXPLAIN 是一對(duì)黃金搭檔。前者負(fù)責(zé)"發(fā)現(xiàn)",后者負(fù)責(zé)"診斷"。發(fā)現(xiàn)問(wèn)題是第一步,解決問(wèn)題是第二步,在后端面試之中遇到“mysql如何調(diào)優(yōu)”這個(gè)問(wèn)題,我們就可以采用這個(gè)思路進(jìn)行回答。在實(shí)際工作生產(chǎn)環(huán)境中,我們依然使用這個(gè)思路進(jìn)行實(shí)際的排查優(yōu)化。
以上就是MySQL慢查詢(xún)?nèi)罩鹃_(kāi)啟與使用:找到那些偷偷變慢的SQL的詳細(xì)內(nèi)容,更多關(guān)于MySQL慢查詢(xún)?nèi)罩镜馁Y料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- MySQL使用慢查詢(xún)?nèi)罩維low Log定位低效SQL的全過(guò)程
- 從配置到性能優(yōu)化全面解析MySQL慢查詢(xún)?nèi)罩?/a>
- MySQL慢查詢(xún)?nèi)罩緩呐渲玫絻?yōu)化實(shí)踐全解析
- 一文帶大家深入了解下MySQL中的慢查詢(xún)?nèi)罩?/a>
- MySQL 慢查詢(xún)?nèi)罩?、日志分析工具mysqldumpslow示例詳解
- MySQL日志系統(tǒng)之錯(cuò)誤日志、慢查詢(xún)?nèi)罩?、二進(jìn)制日志詳解
- MySQL慢查詢(xún)?nèi)罩?Slow Query Log)的實(shí)現(xiàn)
相關(guān)文章
MySQL遷移KingbaseESV8R2的實(shí)現(xiàn)步驟
本文主要介紹了MySQL遷移KingbaseESV8R2的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
DBeaver連接mysql數(shù)據(jù)庫(kù)圖文教程(超詳細(xì))
本文主要介紹了DBeaver連接mysql數(shù)據(jù)庫(kù)圖文教程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
SQL調(diào)優(yōu)實(shí)戰(zhàn)之讓查詢(xún)效率飆升10倍的實(shí)用技巧
在數(shù)據(jù)洪流時(shí)代,企業(yè)每1毫秒的查詢(xún)延遲都可能造成百萬(wàn)級(jí)營(yíng)收損失,本文將結(jié)合18個(gè)真實(shí)案例與28段代碼示例,揭示從索引設(shè)計(jì)到執(zhí)行計(jì)劃分析的完整優(yōu)化鏈路,助你掌握讓查詢(xún)效率提升10倍的核心方法,實(shí)現(xiàn)降本增效的技術(shù)躍遷2026-01-01
MySQL中使用SQL語(yǔ)句查看某個(gè)表的編碼方法
下面小編就為大家?guī)?lái)一篇MySQL中使用SQL語(yǔ)句查看某個(gè)表的編碼方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-11-11
mysql查詢(xún)當(dāng)天的數(shù)據(jù)
這篇文章主要介紹了mysql查詢(xún)當(dāng)天的數(shù)據(jù),第一種數(shù)量小的時(shí)候用,數(shù)據(jù)量稍微起來(lái)巨慢,第二種速度快,但是最好配合復(fù)合索引來(lái)查,避免全表掃描,需要的朋友可以參考下2023-08-08
mysql 8.0.16 Win10 zip版本安裝配置圖文教程
這篇文章主要為大家詳細(xì)介紹了mysql 8.0 Win10 zip版本安裝配置圖文教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-06-06

