首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

MySQL 連接查詢排序優化:先排序後關聯,效能提升 20 倍實戰

     編輯:MySQL 資料庫相關知識

多表關聯查詢帶 ORDER BY ... LIMIT N 時,MySQL 優化器傾向先 Join 再排序,導致中間結果集膨脹、filesort 開銷巨大。本文通過實測對比演示「先排序取 Top-N 再 Join」的重寫模式,實現從 3 秒降至 0.16 秒的 20 倍效能飛躍。

場景與基線測試

-- 表結構
CREATE TABLE t_team (id INT PK, tname VARCHAR(100));
CREATE TABLE t_people (id INT PK, pname VARCHAR(100), team_id INT, FK(team_id) REFERENCES t_team(id));

-- 數據量:team 1,000 行,people 100,000 行

-- 查詢:按人名排序,取前 10 人及其所屬團隊名

寫法 ①:直觀但極慢(3 秒)

SELECT * 
FROM t_people p 
LEFT JOIN t_team t ON p.team_id = t.id 
ORDER BY p.pname 
LIMIT 10;

EXPLAIN:type=ALL(people 全掃),Extra=Using temporary; Using filesort。先 Join 產生 10 萬行中間表,再排序,再取 10 行。

寫法 ②:去掉 ORDER BY(0.00 秒)

SELECT * FROM t_people p LEFT JOIN t_team t ON p.team_id = t.id LIMIT 10;

無排序,直接取前 10 行 Join,極快。

寫法 ③:去掉 JOIN,只排序 people(0.15 秒)

SELECT * FROM t_people p ORDER BY p.pname LIMIT 10;

單表索引排序(pname 有索引),極快。

優化重寫:寫法 ④ 先排序後 Join(0.16 秒,快 20 倍)

SELECT * 
FROM (
    SELECT * FROM t_people p ORDER BY p.pname LIMIT 10
) AS p 
LEFT JOIN t_team t ON p.team_id = t.id 
LIMIT 10;  -- 外層 LIMIT 保障語義

執行邏輯:

  1. 衍生表:SELECT * FROM t_people ORDER BY pname LIMIT 10 → 走 pname 索引,僅讀 10 行,生成 10 行臨時表。
  2. 外層:10 行臨時表 LEFT JOIN t_team → 10 次主鍵查找,極快。

EXPLAIN 顯示:衍生表 select_type=DERIVED、type=index、rows=10;外層 type=eq_ref。

實戰驗證:複雜業務場景

「我在實際開發中就碰到了這樣的問題,使用寫法 ① 耗時 80 多秒,但使用寫法 ④ 只需 1 秒以內。」

表結構越複雜(多表 Join、寬字段、大中間集),優化收益越明顯。

適用條件與邊界

條件說明
ORDER BY 列屬於驅動表示例中 p.pname 屬於 t_people(驅動表),可先排序。若按被驅動表列排序(ORDER BY t.tname),需調整驅動表或用其他策略。
驅動表有排序索引pname 有索引,否則衍生表仍需 filesort,收益遞減但通常仍優於全 Join 排序。
LIMIT 較小Top-N 查詢(N < 1000)效果最佳。大 LIMIT 接近全表時,優化器可能自動選擇更好計劃。
無 GROUP BY / DISTINCT 干擾衍生表內若含聚合/去重,需驗證語義等價性。

進階:MySQL 8.0 CTE 寫法(更優雅)

WITH top_people AS (
    SELECT * FROM t_people ORDER BY pname LIMIT 10
)
SELECT tp.*, t.tname 
FROM top_people tp 
LEFT JOIN t_team t ON tp.team_id = t.id;

CTE(Common Table Expression)語義等同衍生表,可讀性更強,8.0+ 推薦使用。

總結

連接查詢 + ORDER BY + LIMIT 性能殺手鐵律:
1. 先在驅動表上排序取 Top-N(衍生表/CTE)。
2. 再用小結果集 Join 其它表。
3. 驗證 EXPLAIN:衍生表 rows=N,外層 type=eq_ref/ref。
這是「將排序下推到單表」的經典模式,適用于 MySQL 5.6+ 所有版本,收益與中間結果集大小正相關。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved