MySQL慢查詢分析與優(yōu)化全過(guò)程
摘要:慢查詢是 MySQL 性能問(wèn)題中最常見、影響最大的殺手。本文從慢查詢?nèi)罩镜呐渲瞄_啟講起,通過(guò) pt-query-digest 定位 TOP 慢 SQL,結(jié)合 EXPLAIN 診斷執(zhí)行計(jì)劃,給出 5 個(gè)真實(shí)優(yōu)化案例(從全表掃描到覆蓋索引、從深分頁(yè)到子查詢改寫),最后搭建 Prometheus + Grafana 監(jiān)控體系,形成完整的慢查詢優(yōu)化閉環(huán)。全文包含實(shí)戰(zhàn)命令和性能對(duì)比數(shù)據(jù),建議收藏。
一、慢查詢優(yōu)化的完整閉環(huán)
慢查詢優(yōu)化不是一次性任務(wù),而是一個(gè)持續(xù)迭代的 PDCA 閉環(huán):
發(fā)現(xiàn) → 定位 → 診斷 → 優(yōu)化 → 驗(yàn)證 → 監(jiān)控 → 再回到發(fā)現(xiàn)
MySQL 查詢執(zhí)行流程如下:客戶端發(fā)送 SQL → 解析器解析 → 預(yù)處理器處理 → 查詢優(yōu)化器生成執(zhí)行計(jì)劃 → 查詢執(zhí)行引擎調(diào)用存儲(chǔ)引擎 → 返回結(jié)果。

理解這個(gè)流程對(duì)診斷慢查詢至關(guān)重要:優(yōu)化器選擇的執(zhí)行計(jì)劃直接決定了查詢效率,而執(zhí)行計(jì)劃又依賴于索引統(tǒng)計(jì)信息和成本估算。
二、第一步:發(fā)現(xiàn)慢查詢
2.1 開啟慢查詢?nèi)罩荆⊿low Query Log)
慢查詢?nèi)罩臼?MySQL 內(nèi)置的慢 SQL 記錄機(jī)制,建議生產(chǎn)環(huán)境必開。
-- 查看當(dāng)前配置 SHOW VARIABLES LIKE 'slow_query%'; SHOW VARIABLES LIKE 'long_query_time'; -- 動(dòng)態(tài)開啟(無(wú)需重啟,但重啟后失效) SET GLOBAL slow_query_log = 'ON'; SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log'; SET GLOBAL long_query_time = 1; -- 超過(guò) 1 秒視為慢查詢 -- 記錄未使用索引的查詢(強(qiáng)烈建議開啟) SET GLOBAL log_queries_not_using_indexes = 'ON';
永久生效配置(my.cnf):
[mysqld] slow_query_log = 1 slow_query_log_file = /var/log/mysql/slow.log long_query_time = 1 log_queries_not_using_indexes = 1 log_output = FILE -- 或 TABLE,記錄到 mysql.slow_log 表
2.2 使用 Performance Schema(MySQL 5.6+)
Performance Schema 提供更精細(xì)的語(yǔ)句級(jí)性能數(shù)據(jù),無(wú)需寫日志文件。
-- 開啟 Statement 監(jiān)控
UPDATE performance_schema.setup_consumers
SET ENABLED = 'YES' WHERE NAME LIKE '%statements%';
-- 查看耗時(shí) TOP 10 的 SQL
SELECT
DIGEST_TEXT AS query,
COUNT_STAR AS exec_count,
ROUND(SUM_TIMER_WAIT/1000000000000, 2) AS total_latency_sec,
ROUND(AVG_TIMER_WAIT/1000000000000, 4) AS avg_latency_sec,
ROUND(MAX_TIMER_WAIT/1000000000000, 4) AS max_latency_sec
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10;

Performance Schema 的數(shù)據(jù)實(shí)時(shí)在內(nèi)存中維護(hù),適合需要即時(shí)分析的場(chǎng)景,但重啟后數(shù)據(jù)會(huì)丟失。
2.3 使用 sys 系統(tǒng)庫(kù)(MySQL 5.7+)
sys 庫(kù)基于 Performance Schema 提供了更友好的視圖:
-- 查看全表掃描次數(shù)最多的 SQL SELECT * FROM sys.statements_with_full_table_scans ORDER BY rows_examined DESC LIMIT 10; -- 查看執(zhí)行次數(shù)最多且平均耗時(shí)高的 SQL SELECT * FROM sys.statements_with_runtimes_in_95th_percentile; -- 查看使用臨時(shí)表和文件排序的 SQL SELECT * FROM sys.statements_with_sorting ORDER BY rows_sorted DESC LIMIT 10;
三、第二步:定位 TOP 慢 SQL
3.1 pt-query-digest:慢日志分析神器
pt-query-digest 是 Percona Toolkit 中的慢查詢分析工具,能將混亂的慢日志整理成清晰的排名報(bào)告。
安裝:
# Ubuntu/Debian apt-get install percona-toolkit # CentOS/RHEL yum install percona-toolkit # 或下載源碼包 wget https://www.percona.com/downloads/percona-toolkit/3.5.5/binary/tarball/percona-toolkit-3.5.5_x86_64.tar.gz
基本用法:
# 分析慢查詢?nèi)罩?,輸出到文?
pt-query-digest /var/log/mysql/slow.log > slow_report.txt
# 只顯示前 20 條(默認(rèn)按總耗時(shí)排序)
pt-query-digest --limit 20 /var/log/mysql/slow.log
# 過(guò)濾只分析最近 1 小時(shí)的日志
pt-query-digest --since "1h" /var/log/mysql/slow.log
# 分析特定數(shù)據(jù)庫(kù)的慢查詢
pt-query-digest --filter '$event->{db} eq "mydb"' /var/log/mysql/slow.log
# 直接分析 Processlist(實(shí)時(shí)分析)
pt-query-digest --processlist h=localhost,u=root,p=password3.2 pt-query-digest 報(bào)告核心指標(biāo)解讀

報(bào)告頭部 Overall statistics 匯總了日志整體情況:
| 指標(biāo) | 含義 | 優(yōu)化意義 |
|---|---|---|
| Exec time | SQL 總執(zhí)行時(shí)間 | 找出時(shí)間殺手 |
| Lock time | 等待鎖的時(shí)間 | 高說(shuō)明有鎖競(jìng)爭(zhēng) |
| Rows sent | 返回客戶端的行數(shù) | 高說(shuō)明可能 SELECT * |
| Rows examine | 掃描的行數(shù) | 對(duì)比 Rows sent,比值越大效率越低 |
| Rows affected | 變更的行數(shù) | DML 語(yǔ)句的影響面 |
| Tmp tables | 創(chuàng)建臨時(shí)表次數(shù) | 高說(shuō)明需要優(yōu)化 GROUP BY / ORDER BY |
| Tmp disk tbl | 磁盤臨時(shí)表次數(shù) | 高說(shuō)明內(nèi)存不足,性能急劇下降 |
排名部分關(guān)鍵字段:
| 字段 | 含義 | 判斷標(biāo)準(zhǔn) |
|---|---|---|
| Response time | 該 SQL 總耗時(shí)及占比 | 占比 > 10% 必須優(yōu)化 |
| Calls | 執(zhí)行次數(shù) | 高頻慢查詢影響面更大 |
| R/Call | 平均每次耗時(shí) | > 1s 必須優(yōu)化,> 100ms 建議優(yōu)化 |
| V/M | 響應(yīng)時(shí)間方差均值比 | > 0.1 說(shuō)明執(zhí)行時(shí)間波動(dòng)大,可能存在鎖競(jìng)爭(zhēng) |
分析口訣:先看 Response time 占比找元兇 → 再看 Calls 確認(rèn)影響面 → 最后看 R/Call 評(píng)估單次傷害。
四、第三步:診斷執(zhí)行計(jì)劃
找到 TOP 慢 SQL 后,用 EXPLAIN 診斷其執(zhí)行計(jì)劃。
4.1 EXPLAIN 關(guān)鍵字段速查

上圖展示了 MySQL Workbench 的可視化執(zhí)行計(jì)劃,能直觀看到:
- 全表掃描(Full Table Scan)的代價(jià)
- 嵌套循環(huán)連接(Nested Loop Join)的過(guò)程
- 索引查找(Key Lookup)的效率對(duì)比
核心字段診斷:
| 字段 | 正常值 | 危險(xiǎn)值 | 優(yōu)化方向 |
|---|---|---|---|
| type | ref/range/eq_ref | ALL/index | 創(chuàng)建/調(diào)整索引 |
| key | 有具體索引名 | NULL | 檢查 WHERE 條件是否命中索引 |
| rows | 遠(yuǎn)小于表總行數(shù) | 接近表總行數(shù) | 增加過(guò)濾條件或優(yōu)化索引 |
| Extra | Using index | Using filesort/Using temporary | 利用覆蓋索引、簡(jiǎn)化排序分組 |
4.2 常見執(zhí)行計(jì)劃問(wèn)題診斷
-- 案例:檢查一個(gè)慢查詢的執(zhí)行計(jì)劃 EXPLAIN ANALYZE SELECT o.*, u.name FROM orders o JOIN user u ON o.user_id = u.id WHERE o.status = 'pending' AND o.create_time > '2024-01-01' ORDER BY o.create_time DESC LIMIT 100;
常見問(wèn)題排查:
- type = ALL:全表掃描。檢查是否有合適索引、是否索引失效、是否數(shù)據(jù)量過(guò)大導(dǎo)致優(yōu)化器放棄索引。
- Extra = Using filesort:需要額外排序。嘗試將 ORDER BY 列加入索引,或利用覆蓋索引避免回表后排序。
- Extra = Using temporary:需要?jiǎng)?chuàng)建臨時(shí)表。常見于復(fù)雜 GROUP BY,嘗試簡(jiǎn)化查詢或調(diào)整 GROUP BY 順序匹配索引。
- rows 遠(yuǎn)大于實(shí)際返回?cái)?shù):說(shuō)明掃描了大量無(wú)效數(shù)據(jù)。檢查索引選擇性,或增加更精確的過(guò)濾條件。
五、第四步:優(yōu)化實(shí)戰(zhàn)
案例 1:SELECT * 導(dǎo)致的回表災(zāi)難
場(chǎng)景:訂單查詢頁(yè)面加載緩慢,用戶反饋卡頓。
-- 原始慢查詢 (平均 2.3s) SELECT * FROM orders WHERE user_id = 12345 AND status = 'completed' ORDER BY create_time DESC LIMIT 10;
診斷:
EXPLAIN SELECT * FROM orders WHERE user_id = 12345 AND status = 'completed' ... -- 結(jié)果: type=ref, key=idx_user_id, rows=15000, Extra=Using where; Using filesort
問(wèn)題分析:
- 索引
idx_user_id只包含user_id,過(guò)濾后仍有 15000 行 SELECT *導(dǎo)致回表 15000 次ORDER BY create_time需要額外排序(filesort)
優(yōu)化方案:
-- 創(chuàng)建覆蓋索引,包含 WHERE、ORDER BY、SELECT 需要的所有列 CREATE INDEX idx_user_status_time ON orders(user_id, status, create_time, order_no, amount, pay_time); -- 改寫查詢,只取必要字段(利用覆蓋索引) SELECT order_no, amount, status, create_time, pay_time FROM orders WHERE user_id = 12345 AND status = 'completed' ORDER BY create_time DESC LIMIT 10;
優(yōu)化后 EXPLAIN:
type=ref, key=idx_user_status_time, rows=120, Extra=Using index
效果:查詢耗時(shí)從 2.3s → 12ms,提升 190 倍。
案例 2:深分頁(yè) LIMIT 100000, 10 的性能陷阱
場(chǎng)景:后臺(tái)管理系統(tǒng)翻頁(yè)到第 10000 頁(yè)后,頁(yè)面加載超過(guò) 5 秒。
-- 原始慢查詢 (平均 4.8s) SELECT * FROM orders WHERE status = 'completed' ORDER BY create_time DESC LIMIT 100000, 10;
診斷:
type=index, key=idx_status_time, rows=100010, Extra=Using where
問(wèn)題分析:MySQL 的 LIMIT 實(shí)現(xiàn)是「先掃描 100010 行,再丟棄前 100000 行」,越往后越慢。
優(yōu)化方案 1:延遲關(guān)聯(lián)(Deferred Join)
-- 先查主鍵,再回表取數(shù)據(jù)
SELECT o.*
FROM orders o
JOIN (
SELECT id
FROM orders
WHERE status = 'completed'
ORDER BY create_time DESC
LIMIT 100000, 10
) tmp ON o.id = tmp.id;
優(yōu)化方案 2:基于游標(biāo)的分頁(yè)(推薦)
-- 上一頁(yè)最后一條記錄的 create_time 為 '2024-03-15 14:30:00' SELECT * FROM orders WHERE status = 'completed' AND create_time < '2024-03-15 14:30:00' ORDER BY create_time DESC LIMIT 10;
效果:方案 2 從 4.8s → 15ms,且性能不隨頁(yè)碼增加而下降。
案例 3:隱式類型轉(zhuǎn)換導(dǎo)致索引失效
場(chǎng)景:手機(jī)號(hào)查詢接口偶發(fā)卡頓,有時(shí) 50ms,有時(shí) 3s。
-- 表結(jié)構(gòu)
CREATE TABLE user (
id INT PRIMARY KEY,
phone VARCHAR(20), -- 注意是 VARCHAR
INDEX idx_phone (phone)
);
-- 原始查詢(Java 代碼傳入 long 類型)
SELECT * FROM user WHERE phone = 13800138000;
診斷:
EXPLAIN SELECT * FROM user WHERE phone = 13800138000; -- 結(jié)果: type=ALL, key=NULL, rows=5000000
問(wèn)題分析:phone 是 VARCHAR,傳入數(shù)字時(shí) MySQL 隱式將 phone 列轉(zhuǎn)換為數(shù)字再比較,相當(dāng)于對(duì)索引列做了函數(shù)處理,導(dǎo)致索引失效。
優(yōu)化方案:
// 修改 Java 代碼,確保傳入字符串
String phone = "13800138000"; // 原來(lái)是 Long phone = 13800138000L;
jdbcTemplate.query("SELECT * FROM user WHERE phone = ?", phone);效果:查詢耗時(shí)從 3s → 5ms。
案例 4:OR 條件導(dǎo)致的全表掃描
場(chǎng)景:訂單搜索功能支持按訂單號(hào)或手機(jī)號(hào)查詢,慢查詢?nèi)罩局性?SQL 頻繁出現(xiàn)。
-- 原始慢查詢 (平均 1.5s) SELECT * FROM orders WHERE order_no = 'ORD2024001' OR user_phone = '13800138000';
診斷:
type=ALL, key=NULL, rows=10000000
問(wèn)題分析:OR 兩邊條件分別適合不同索引(idx_order_no 和 idx_user_phone),但優(yōu)化器選擇全表掃描。
優(yōu)化方案:拆分為 UNION ALL
-- 優(yōu)化后查詢 (平均 15ms) SELECT * FROM orders WHERE order_no = 'ORD2024001' UNION ALL SELECT * FROM orders WHERE user_phone = '13800138000' AND order_no <> 'ORD2024001'; -- 避免重復(fù)
效果:從 1.5s → 15ms,提升 100 倍。
案例 5:統(tǒng)計(jì)查詢的索引優(yōu)化與緩存策略
場(chǎng)景:首頁(yè) Dashboard 需要實(shí)時(shí)統(tǒng)計(jì)今日訂單金額和數(shù)量,每刷新一次就查一次數(shù)據(jù)庫(kù)。
-- 原始慢查詢 (平均 800ms,并發(fā)高時(shí)更慢)
SELECT
COUNT(*) AS order_count,
SUM(amount) AS total_amount,
AVG(amount) AS avg_amount
FROM orders
WHERE create_time >= CURDATE();
診斷:
type=range, key=idx_create_time, rows=50000, Extra=Using index condition
問(wèn)題分析:雖然走了索引,但掃描行數(shù)多,且是聚合計(jì)算,CPU 開銷大。高并發(fā)時(shí)成為瓶頸。
優(yōu)化方案 1:覆蓋索引
-- 創(chuàng)建覆蓋索引(只包含查詢需要的列) CREATE INDEX idx_create_time_amount ON orders(create_time, amount); -- 查詢變?yōu)楦采w索引掃描 SELECT COUNT(*), SUM(amount), AVG(amount) FROM orders WHERE create_time >= CURDATE();
優(yōu)化方案 2:冗余統(tǒng)計(jì)表(最終采用)
-- 創(chuàng)建統(tǒng)計(jì)表
CREATE TABLE order_daily_stats (
stat_date DATE PRIMARY KEY,
order_count INT,
total_amount DECIMAL(18,2),
avg_amount DECIMAL(18,2),
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 通過(guò)定時(shí)任務(wù)或觸發(fā)器更新(每 5 分鐘)
INSERT INTO order_daily_stats (stat_date, order_count, total_amount, avg_amount)
SELECT CURDATE(), COUNT(*), SUM(amount), AVG(amount)
FROM orders
WHERE create_time >= CURDATE()
ON DUPLICATE KEY UPDATE
order_count = VALUES(order_count),
total_amount = VALUES(total_amount),
avg_amount = VALUES(avg_amount);
-- 查詢變?yōu)楹撩爰?jí)
SELECT * FROM order_daily_stats WHERE stat_date = CURDATE();
效果:從 800ms → 2ms,且不受并發(fā)影響。
六、第五步:驗(yàn)證優(yōu)化效果
優(yōu)化后必須驗(yàn)證,避免引入新問(wèn)題。
6.1 EXPLAIN 對(duì)比驗(yàn)證
-- 優(yōu)化前保存執(zhí)行計(jì)劃 EXPLAIN SELECT ... \G -- 保存輸出 -- 優(yōu)化后對(duì)比 EXPLAIN SELECT ... \G -- 確認(rèn) type、rows、Extra 改善
6.2 性能測(cè)試
# 使用 mysqlslap 壓測(cè) mysqlslap --concurrency=50 --iterations=10 \ --query="SELECT order_no, amount FROM orders WHERE user_id = 12345 AND status = 'completed' ORDER BY create_time DESC LIMIT 10" \ --create-schema=mydb --delimiter=";" \ --engine=innodb --number-of-queries=1000 # 或使用 sysbench sysbench oltp_read_only --mysql-host=localhost --mysql-user=root \ --mysql-password=xxx --mysql-db=mydb --tables=10 --table-size=1000000 \ --threads=64 --time=60 --report-interval=10 run
6.3 生產(chǎn)灰度驗(yàn)證
-- 使用 MySQL 8.0 的 Query Rewrite 插件,先對(duì)部分流量生效
INSTALL PLUGIN query_rewrite SONAME 'rewriter.so';
-- 添加重寫規(guī)則(先對(duì) 10% 流量生效,驗(yàn)證無(wú)誤后再全量)
INSERT INTO query_rewrite.rewrite_rules (pattern, replacement, enabled)
VALUES (
'SELECT * FROM orders WHERE user_id = ? AND status = ? ORDER BY create_time DESC LIMIT ?',
'SELECT order_no, amount, status, create_time, pay_time FROM orders WHERE user_id = ? AND status = ? ORDER BY create_time DESC LIMIT ?',
'YES'
);
CALL query_rewrite.flush_rewrite_rules();
七、第六步:搭建監(jiān)控體系
7.1 Prometheus + mysqld_exporter 監(jiān)控
# docker-compose.yml
version: '3'
services:
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
mysqld_exporter:
image: prom/mysqld-exporter
environment:
- DATA_SOURCE_NAME=root:password@(mysql:3306)/
ports:
- "9104:9104"
grafana:
image: grafana/grafana
ports:
- "3000:3000"關(guān)鍵監(jiān)控指標(biāo):
| 指標(biāo) | PromQL | 告警閾值 |
|---|---|---|
| 慢查詢速率 | rate(mysql_global_status_slow_queries[5m]) | > 10/min |
| 平均查詢耗時(shí) | mysql_global_status_slow_queries / mysql_global_status_queries | > 1% |
| 全表掃描次數(shù) | rate(mysql_global_status_select_scan[5m]) | > 100/min |
| 活躍連接數(shù) | mysql_global_status_threads_running | > 80% max_connections |
| 鎖等待時(shí)間 | mysql_global_status_innodb_row_lock_waits | > 50/min |

上圖展示了 Grafana 中 MySQL 監(jiān)控看板的典型布局,可以直觀看到:
- 系統(tǒng)指標(biāo)(CPU、內(nèi)存、磁盤 IO)
- MySQL 指標(biāo)(延遲、QPS、慢查詢數(shù)、活躍連接數(shù))
- 異??蛻舳诉B接預(yù)警
7.2 慢查詢自動(dòng)巡檢腳本
#!/bin/bash
# slow_query_check.sh - 每日慢查詢巡檢
LOG_FILE="/var/log/mysql/slow.log"
REPORT_FILE="/tmp/slow_report_$(date +%Y%m%d).txt"
THRESHOLD=10 # 慢查詢數(shù)量閾值
# 生成報(bào)告
pt-query-digest --limit 20 $LOG_FILE > $REPORT_FILE
# 提取 TOP 1 的耗時(shí)占比
TOP_PCT=$(grep -A 1 "Rank Query ID" $REPORT_FILE | head -3 | tail -1 | awk '{print $3}')
# 發(fā)送告警
if [ $(echo "$TOP_PCT > $THRESHOLD" | bc) -eq 1 ]; then
curl -X POST "https://oapi.dingtalk.com/robot/send?access_token=xxx" \
-H "Content-Type: application/json" \
-d "{"msgtype": "markdown", "markdown": {"title": "MySQL慢查詢告警", "text": "### MySQL 慢查詢告警\nTOP 1 慢查詢占比: ${TOP_PCT}%\n請(qǐng)查看報(bào)告: ${REPORT_FILE}"}}"
fi
# 歸檔日志(可選)
mv $LOG_FILE /var/log/mysql/slow_$(date +%Y%m%d).log
mysqladmin -uroot -p flush-logs slow八、慢查詢優(yōu)化總結(jié):決策矩陣
| 問(wèn)題類型 | 診斷特征 | 優(yōu)化手段 | 預(yù)期效果 |
|---|---|---|---|
| 全表掃描 | type=ALL, rows≈總行數(shù) | 創(chuàng)建索引、調(diào)整 WHERE 條件 | 10~1000 倍提升 |
| 回表過(guò)多 | Extra=Using where, key 有值 | 覆蓋索引、減少 SELECT 字段 | 5~50 倍提升 |
| 文件排序 | Extra=Using filesort | 索引包含 ORDER BY 列、利用覆蓋索引 | 3~20 倍提升 |
| 深分頁(yè) | LIMIT 100000+, rows 巨大 | 延遲關(guān)聯(lián)、游標(biāo)分頁(yè)、ES 替代 | 100~1000 倍提升 |
| 隱式轉(zhuǎn)換 | key=NULL, 列類型與傳入值不匹配 | 保持類型一致、代碼層面修復(fù) | 10~1000 倍提升 |
| OR 失效 | type=ALL, 多個(gè) OR 條件 | UNION ALL 拆分、分別走索引 | 10~100 倍提升 |
| 聚合統(tǒng)計(jì) | 大表 COUNT/SUM/AVG | 覆蓋索引、冗余統(tǒng)計(jì)表、緩存 | 10~500 倍提升 |
| 鎖競(jìng)爭(zhēng) | Lock time 高, V/M > 0.1 | 減少事務(wù)范圍、調(diào)整隔離級(jí)別、優(yōu)化索引 | 2~10 倍提升 |
九、面試高頻考點(diǎn)速記
Q1:如何定位生產(chǎn)環(huán)境的慢查詢?
- 開啟 slow_query_log 和 log_queries_not_using_indexes
- 使用 pt-query-digest 分析慢日志,按 Response time 排序找 TOP SQL
- 結(jié)合 Performance Schema 的 events_statements_summary_by_digest 查看實(shí)時(shí)數(shù)據(jù)
- 使用 sys 庫(kù)的 statements_with_full_table_scans 等視圖快速定位問(wèn)題
Q2:pt-query-digest 報(bào)告中的 V/M 是什么意思?
V/M 是響應(yīng)時(shí)間的方差均值比(Variance-to-Mean ratio)。值越大說(shuō)明 SQL 執(zhí)行時(shí)間波動(dòng)越大,可能存在鎖競(jìng)爭(zhēng)、數(shù)據(jù)分布不均或緩存命中率低的問(wèn)題。V/M > 0.1 需要關(guān)注。
Q3:LIMIT 100000, 10 為什么慢?如何優(yōu)化?
MySQL 的 LIMIT 實(shí)現(xiàn)是先掃描 offset + limit 行,再丟棄 offset 行。深分頁(yè)時(shí)掃描量巨大。
優(yōu)化方案:1)延遲關(guān)聯(lián)先查主鍵再回表;2)基于上一頁(yè)最后記錄的游標(biāo)分頁(yè)(推薦);3)使用搜索引擎(ES)替代。
Q4:SELECT * 為什么不好?
- 增加網(wǎng)絡(luò) IO,返回?zé)o用數(shù)據(jù);2. 無(wú)法使用覆蓋索引,必須回表;3. 增加內(nèi)存消耗;4. 表結(jié)構(gòu)變更可能導(dǎo)致應(yīng)用報(bào)錯(cuò)。應(yīng)只查詢需要的字段。
Q5:優(yōu)化后如何驗(yàn)證效果?
- EXPLAIN 對(duì)比執(zhí)行計(jì)劃(type、rows、Extra 改善);2. mysqlslap / sysbench 壓測(cè)對(duì)比 QPS/TPS;3. 生產(chǎn)灰度發(fā)布,觀察慢查詢?nèi)罩竞捅O(jiān)控指標(biāo);4. 關(guān)注業(yè)務(wù)指標(biāo)(頁(yè)面加載時(shí)間、接口成功率)。
結(jié)語(yǔ)
慢查詢優(yōu)化是一個(gè)系統(tǒng)工程:從日志配置到工具分析,從執(zhí)行計(jì)劃診斷到 SQL 改寫,從索引設(shè)計(jì)到架構(gòu)調(diào)整,最后通過(guò)監(jiān)控體系持續(xù)閉環(huán)。
記住三個(gè)核心原則:
- 先定位再優(yōu)化:用數(shù)據(jù)說(shuō)話,不要憑感覺改 SQL
- 索引是銀彈但不是萬(wàn)能:覆蓋索引能解決 80% 的查詢性能問(wèn)題,但深分頁(yè)、聚合統(tǒng)計(jì)需要架構(gòu)層面解決
- 監(jiān)控是閉環(huán)的關(guān)鍵:沒有監(jiān)控的優(yōu)化等于沒優(yōu)化,問(wèn)題會(huì)反復(fù)出現(xiàn)


上圖展示了真實(shí)的優(yōu)化效果對(duì)比:優(yōu)化前數(shù)據(jù)庫(kù)操作耗時(shí)波動(dòng)劇烈(峰值 6s+),優(yōu)化后趨于平穩(wěn)(<1s)。這正是慢查詢優(yōu)化帶來(lái)的直接業(yè)務(wù)價(jià)值。
以上就是MySQL慢查詢分析與優(yōu)化全過(guò)程的詳細(xì)內(nèi)容,更多關(guān)于MySQL慢查詢分析與優(yōu)化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Mysql更新varchar存儲(chǔ)Json數(shù)據(jù)的操作方法
這篇文章主要介紹了Mysql更新varchar存儲(chǔ)Json數(shù)據(jù)的操作方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-12-12
簡(jiǎn)單談?wù)凪ySQL5.7 JSON格式檢索
MySQL 5.7.7 labs版本開始InnoDB存儲(chǔ)引擎已經(jīng)原生支持JSON格式,該格式不是簡(jiǎn)單的BLOB類似的替換。下面我們來(lái)詳細(xì)探討下吧2017-01-01
mysql實(shí)現(xiàn)將date字段默認(rèn)值設(shè)置為CURRENT_DATE
這篇文章主要介紹了mysql實(shí)現(xiàn)將date字段默認(rèn)值設(shè)置為CURRENT_DATE問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
如何添加一個(gè)mysql用戶并給予權(quán)限詳解
在很多時(shí)候我們并不會(huì)直接利用mysql的root用戶進(jìn)行項(xiàng)目的開發(fā),一般我們都會(huì)創(chuàng)建一個(gè)具有部分權(quán)限的用戶,下面這篇文章主要給大家介紹了關(guān)于如何添加一個(gè)mysql用戶并給予權(quán)限的相關(guān)資料,需要的朋友可以參考下2023-03-03
mysql-8.0.30壓縮包版安裝和配置MySQL環(huán)境過(guò)程
該文章介紹了如何在Windows系統(tǒng)中下載、安裝和配置MySQL數(shù)據(jù)庫(kù),包括下載地址、解壓文件、創(chuàng)建和配置my.ini文件、設(shè)置環(huán)境變量、初始化MySQL服務(wù)、啟動(dòng)服務(wù)以及修改root用戶密碼等步驟2025-01-01
MySQL系列之開篇 MySQL關(guān)系型數(shù)據(jù)庫(kù)基礎(chǔ)概念
數(shù)據(jù)庫(kù)是指長(zhǎng)期儲(chǔ)存在計(jì)算機(jī)中的有組織的、可共享的數(shù)據(jù)集合,數(shù)據(jù)具有三大基本特點(diǎn),永久存儲(chǔ),有組織,可共享,是數(shù)據(jù)庫(kù)系統(tǒng)的核心,本文給大家分享MySQL關(guān)系型數(shù)據(jù)庫(kù)基礎(chǔ)概念,需要的朋友參考下吧2021-07-07
MySQL的兩種分頁(yè)方式之Offset/Limit分頁(yè)和游標(biāo)分頁(yè)詳解
這篇文章主要對(duì)比了MySQL的Offset/Limit分頁(yè)與游標(biāo)分頁(yè),指出前者簡(jiǎn)單但存在數(shù)據(jù)漂移和性能缺陷,后者通過(guò)游標(biāo)避免這些問(wèn)題且更高效,建議根據(jù)業(yè)務(wù)場(chǎng)景選擇分頁(yè)方式,深度分頁(yè)或動(dòng)態(tài)數(shù)據(jù)宜用游標(biāo)分頁(yè),而延遲聯(lián)結(jié)可優(yōu)化Offset/Limit性能,需要的朋友可以參考下2025-09-09
MySQL數(shù)據(jù)庫(kù)事務(wù)原理及應(yīng)用
MySQL數(shù)據(jù)庫(kù)事務(wù)是指一組數(shù)據(jù)庫(kù)操作,要么全部執(zhí)行成功,要么全部回滾。事務(wù)可以確保數(shù)據(jù)的一致性和完整性,避免了多個(gè)用戶同時(shí)對(duì)同一數(shù)據(jù)進(jìn)行修改所帶來(lái)的問(wèn)題。MySQL通過(guò)事務(wù)日志記錄事務(wù)的操作,支持事務(wù)的回滾和提交等操作2023-04-04

