sql調優
線上發現部分sql查詢時間過長。使用explain觀察是否命中表的索引。未命中索引,使用 TABLE add index 語句添加索引。
除此之外,單個字段命中聯合索引的情況也會導致查詢變慢
針對多個字段的查詢可添加聯合索引。
總結如下慢sql的原因:
一、查詢語句本身的問題
查詢語句的編寫是否高效,直接影響執行效率,常見問題包括:
- 未使用索引,或條件無法命中索引時
- 當查詢未使用索引,或條件無法命中索引時,數據庫會遍歷整個表的所有行,尤其對于大表(百萬級以上數據),耗時會急劇增加。
- 例:
SELECT * FROM orders WHERE amount > 1000;
若amount
字段無索引,會觸發全表掃描。
- 使用
SELECT *
查詢所有列 - 復雜的聯表查詢(JOIN)
- 多表聯表時未指定有效的關聯條件(如
ON
子句缺失或不合理),導致產生笛卡爾積(行數呈指數級增長)。 - 聯表順序不合理:數據庫優化器可能因表大小或統計信息不準確,選擇低效的聯表順序(如小表驅動大表更高效,反之則耗時)。
- 多表聯表時未指定有效的關聯條件(如
- 子查詢嵌套過深
- 多層嵌套的子查詢(尤其是
IN
子句中的子查詢)可能被數據庫多次執行,而非一次性優化,導致重復計算。 - 例:
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders WHERE status=1);
若子查詢返回大量數據,效率會很低。同理避免in()中有大量數據。
- 多層嵌套的子查詢(尤其是
- 排序和分組操作低效
ORDER BY
、GROUP BY
、DISTINCT
等操作需要對數據進行排序或聚合,若涉及數據量過大且無索引支持,會觸發臨時表或文件排序(磁盤IO耗時遠高于內存)。
二、索引相關問題
索引是提升查詢效率的核心,但不合理的索引設計或使用會適得其反:
- 缺少必要的索引
- 查詢的過濾條件(
WHERE
)、聯表條件(JOIN ON
)、排序字段(ORDER BY
)等未建立索引,導致全表掃描或低效掃描。
- 查詢的過濾條件(
- 索引失效
- 索引字段被函數或表達式操作:如
WHERE SUBSTR(name, 1, 1) = 'A'
,會使索引失效。 - 使用不等號(
!=
、<>
)、NOT IN
、IS NULL
(部分數據庫)、模糊查詢前綴帶%
(如LIKE '%abc'
),可能導致索引失效。 - 聯合索引未遵循“最左前綴原則”:如聯合索引
(a, b, c)
,查詢條件僅用b
或c
時,無法命中索引。
- 索引字段被函數或表達式操作:如
- 索引過多
- 表上索引數量過多,會導致
INSERT
、UPDATE
、DELETE
操作變慢(因為每次修改數據需同步更新索引),同時索引本身會占用磁盤空間,增加查詢時的索引選擇成本。
- 表上索引數量過多,會導致
- 索引碎片化
- 頻繁的更新或刪除操作會導致索引頁碎片化(存儲空間不連續),查詢時需要多次讀取磁盤,降低效率。
三、數據庫設計問題
不合理的表結構或數據分布會長期影響查詢性能:
- 頻繁的更新或刪除操作會導致索引頁碎片化(存儲空間不連續),查詢時需要多次讀取磁盤,降低效率。
- 表結構設計不合理
- 表字段過多(寬表)或字段類型不合適(如用
VARCHAR(255)
存儲手機號,而非CHAR(11)
),導致單條記錄體積過大,掃描時IO成本高。 - 未進行分表分庫:大表(如千萬級以上數據)未按時間、地區等維度拆分,單表數據量過大,查詢耗時自然增加。
- 表字段過多(寬表)或字段類型不合適(如用
- 數據分布不均
- 表中存在“熱點數據”(如某類記錄占比90%以上),即使有索引,查詢這類數據時仍需掃描大量索引頁(類似全表掃描)。
- 例:
WHERE status=0
,若90%的記錄status
都是0,索引可能失效,轉為全表掃描。
- 缺少分區表設計
- 對于時間序列數據(如日志、訂單),未按時間分區(如按月份),查詢歷史數據時仍需掃描全表,而分區表可僅掃描目標分區。
四、數據庫配置與狀態問題
數據庫的運行狀態和配置參數也會影響查詢效率:
- 對于時間序列數據(如日志、訂單),未按時間分區(如按月份),查詢歷史數據時仍需掃描全表,而分區表可僅掃描目標分區。
- 統計信息過時
- 數據庫優化器依賴表的統計信息(如行數、字段分布、索引基數)生成執行計劃,若統計信息過時(如長期未更新),可能選擇低效計劃(如錯誤地走全表掃描而非索引)。
- 解決:定期執行
ANALYZE TABLE
(MySQL)或UPDATE STATISTICS
(SQL Server)更新統計信息。
- 連接數或資源限制
- 數據庫連接數已滿,查詢需等待釋放連接,導致“排隊耗時”。
- 內存、CPU資源不足:查詢需要的內存(如排序緩存、連接緩存)被耗盡,被迫使用磁盤臨時表,或CPU被其他進程占用,處理速度下降。
- 鎖等待或事務阻塞
- 若查詢涉及的表或行被其他事務鎖定(如
SELECT ... FOR UPDATE
未及時提交),當前查詢會進入等待狀態,直到鎖釋放,表現為“查詢超時”。
五、硬件與環境問題
底層硬件或部署環境的瓶頸也可能導致查詢緩慢:
- 若查詢涉及的表或行被其他事務鎖定(如
- 磁盤IO性能不足
- 機械硬盤(HDD)讀寫速度遠低于固態硬盤(SSD),若數據庫文件存儲在HDD,且存在大量隨機IO(如索引掃描),會成為瓶頸。
- 磁盤空間不足,導致臨時文件讀寫效率下降。
- 內存不足
- 數據庫緩存(如MySQL的InnoDB Buffer Pool、Oracle的SGA)不足,頻繁將數據從磁盤加載到內存,增加IO次數。
總結
SQL查詢慢的原因可歸納為:查詢不優、索引不當、表設計不合理、數據庫狀態異常、硬件資源不足。排查時可通過執行計劃(如EXPLAIN
命令)分析查詢路徑,結合數據庫監控工具(如MySQL的SHOW PROCESSLIST
、SQL Server的 Profiler)定位具體瓶頸,再針對性優化(如加索引、改寫查詢、分表分庫等)。
- 數據庫緩存(如MySQL的InnoDB Buffer Pool、Oracle的SGA)不足,頻繁將數據從磁盤加載到內存,增加IO次數。