分頁查詢是 Web 應用標配,LIMIT offset, rows 雖簡單,但大偏移量會觸發全表掃描、臨時表、filesort 等效能殺手。本文揭示 LIMIT 內部機制,給出五大進階優化技巧。
LIMIT 0 快速驗證 SQL 有效性與表結構SELECT * FROM huge_table WHERE complex_condition LIMIT 0;
不返回任何行,但會:
EXPLAIN 外快速獲取列類型、索引可用性等元信息。注意: 部分監控工具(如 MySQL Monitor)不支援此語法,僅返回 Empty set。
LIMIT + GROUP BY 共用排序,避免重複計算需求:學生總分排名前 50。
SELECT student_id, SUM(score) AS total
FROM scores
GROUP BY student_id
ORDER BY total DESC
LIMIT 50;
MySQL 優化器識別出 GROUP BY 需排序、ORDER BY 同列,會單次排序完成聚合與截取,省去「先建視圖聚合、再查詢截取」的雙重開銷。若 GROUP BY 列有索引,更可直接索引順序掃描,避免排序。
SQL_CALC_FOUND_ROWS 一次查詢得總數與分頁需同時獲取「前 50 名」與「總符合條件記錄數」(如分頁組件需總頁數)。
SELECT SQL_CALC_FOUND_ROWS * FROM scores WHERE class='A' ORDER BY score DESC LIMIT 50;
SELECT FOUND_ROWS(); -- 返回不帶 LIMIT 的總行數
代價: 該查詢無法使用 Query Cache,複雜 WHERE 時才建議使用;簡單查詢改用 COUNT(*) 獨立查詢更划算。
LIMIT + DISTINCT 去重截取,單語句搞定需求:缺勤次數 Top 20 員工,防重複打卡記錄干擾。
SELECT DISTINCT emp_id, COUNT(*) AS absent_cnt
FROM attendance
WHERE status='absent'
GROUP BY emp_id
ORDER BY absent_cnt DESC
LIMIT 20;
MySQL 會在去重聚合過程中持續排序,一旦收集到 20 條唯一結果即停止,避免全量去重後再截取。
LIMIT + 索引 + ORDER BY 早期終止排序核心原理:索引天然有序。若 ORDER BY col LIMIT 10 且 col 有索引,MySQL 只需沿索引讀取前 10 條即返回,完全跳過排序。
-- 最佳模式:覆蓋索引 + LIMIT
SELECT id, name FROM users WHERE status=1 ORDER BY created_at DESC LIMIT 20;
-- 索引:INDEX idx_status_created (status, created_at DESC)
即使需 filesort,MySQL 也會在找到前 LIMIT 行後立即終止排序,不處理剩餘數據。但前提是 sort_buffer_size 足夠,否則退化磁碟臨時表,早期終止優勢盡失。
LIMIT 1000000, 20 會掃描 100 萬行再丟棄。解法:
WHERE id > last_seen_id ORDER BY id LIMIT 20(需唯一遞增鍵)。SELECT * FROM t1 JOIN (SELECT id FROM t1 WHERE cond ORDER BY col LIMIT 1000000, 20) t2 USING (id),只掃描索引再回表 20 行。LIMIT 優化核心三句話:用索引避免排序、用覆蓋索引避免回表、用游標避免大偏移。配合 EXPLAIN 觀察 Using index、Using filesort、Using temporary 三大標記,逐個擊破,分頁查詢穩如泰山。