數據庫性能瓶頸多半源於 SQL 語句效率低下。本文結合實戰案例,系統梳理 MySQL 慢查詢的定位工具、索引分析方法、優化策略及架構層面的擴展建議,幫助開發者構建高效能的數據訪問層。
在配置文件 my.cnf 或 my.ini 的 [mysqld] 區塊添加:
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2 # 記錄超過 2 秒的查詢,生產環境建議 1-2 秒
log_queries_not_using_indexes = 1 # 可選:記錄未用索引的查詢
修改後重啟 MySQL 服務生效。也可運行時動態調整:SET GLOBAL long_query_time = 2;
EXPLAIN 深度剖析執行計劃重點關注以下列:
ALL(全表掃描)最差,ref/range 較好,const/eq_ref 最優。Using where、Using index(覆蓋索引)、Using temporary、Using filesort 等提示優化方向。SHOW INDEX FROM tbl_name:查看表索引詳情,關注 Cardinality(基數,唯一值數量)。SHOW ENGINE INNODB STATUS:監控事務、鎖、死鎖等引擎狀態。innodb_monitor 表可將引擎狀態定期寫入錯誤日誌(CREATE TABLE innodb_monitor (a INT) ENGINE=INNODB;)。表 TSK_TASK(約 90 萬行),字段:ID(PK)、MON_TIME(索引)、STATUS_ID(外鍵自動建立索引)。
慢查詢:SELECT * FROM TSK_TASK WHERE STATUS_ID = 1064 AND MON_TIME >= '2007-11-22' AND MON_TIME < '2007-11-23'; 耗時 18 秒,掃描 8.8 萬行。
possible_keys: FK_task_status_id_TO_SYS_HIER_INFO, TSK_TASK_KEY_MON_TIME
key: FK_task_status_id_TO_SYS_HIER_INFO # 選擇了外鍵索引
rows: 276168 # 預估掃描 27 萬行,實則遠超預期
SHOW INDEX 顯示:STATUS_ID 基數僅 16,MON_TIME 基數 13502。MySQL 優化器傾向選擇基數較小的索引,導致大量回表掃描。
| 方案 | 操作 | 適用場景 | 效果 |
|---|---|---|---|
| 刪除外鍵索引 | DROP INDEX FK_task_status_id_TO_SYS_HIER_INFO ON TSK_TASK; | 單日任務量較少 | 強制走 MON_TIME 索引,先按時間範圍縮小結果集再過濾狀態 |
| 建立聯合索引 | DROP INDEX ...; CREATE INDEX idx_status_time ON TSK_TASK(STATUS_ID, MON_TIME); | 單日任務量大、高頻查詢 | 索引覆蓋 WHERE 條件,避免回表,效能最優 |
ANALYZE TABLE 更新統計信息至關重要。(STATUS_ID, MON_TIME) 支持 STATUS_ID 單獨查詢及組合查詢,順序不可顛倒。當單庫寫入 QPS 瓶頸無法通過垂直拆分解決時,採用水平分片:
user_id % N 或範圍將數據分散至多個實例,中間件(ShardingSphere、Vitess)路由查詢。expire_logs_days = 7:自動清理舊 Binlog,防止磁盤爆滿。OPTIMIZE TABLE 整理碎片(僅在大量刪除/更新後執行,會鎖表)。Table_locks_waited、Innodb_row_lock_waits 等鎖指標。SELECT *:增加網絡傳輸、緩存失效、覆蓋索引失效風險。LIKE '%keyword%':導致全表掃描,全文搜索請用 Elasticsearch 或 MySQL 全文索引。WHERE phone = 13800138000(phone 為 varchar)導致索引失效,務必加引號。WHERE a=1 OR b=2 通常無法用到聯合索引,改用 UNION ALL。MySQL 慢查詢優化是「工具定位 → 計劃分析 → 索引重構 → 架構演進」的閉環過程。核心原則:讓索引服務於業務查詢模式,而非反過來。大表拒絕外鍵、善用聯合索引、及時分表分庫,配合完善的監控告警體系,才能支撐業務持續增長。