MySQL LIMIT M, N 需要掃描並丟棄前 M 行。當 M 很大時,即使只需 N 行結果,引擎仍需遍歷 M 行索引/數據頁,導致 I/O 與 CPU 浪費。
SELECT * FROM t1 INNER JOIN (SELECT id FROM t1 ORDER BY id DESC LIMIT 935500, 10) t2 USING (id);
優勢:子查詢僅掃描主鍵索引(覆蓋索引),外層通過 eq_ref 精準回表 10 行。
-- 第 1 頁(id 遞增) SELECT * FROM t WHERE id > 0 ORDER BY id ASC LIMIT 100; -- 第 2 頁(記住上頁最大 id = 99) SELECT * FROM t WHERE id > 99 ORDER BY id ASC LIMIT 100; -- 第 3 頁(記住上頁最大 id = 199) SELECT * FROM t WHERE id > 199 ORDER BY id ASC LIMIT 100;
核心優勢:完全避開 OFFSET,利用主鍵聚簇索引定位,掃描行數恆等於 LIMIT N,性能與頁碼無關。
場景:按 column1 排序分頁,但 column1 存在大量重複值(如 11 行同為 100)。
-- 第 1 頁 SELECT * FROM t ORDER BY column1 DESC, id DESC LIMIT 10; -- 第 2 頁(假設上頁最後一行:column1=100, id=500) SELECT * FROM t WHERE column1 < 100 OR (column1 = 100 AND id < 500) ORDER BY column1 DESC, id DESC LIMIT 10;
進一步優化為 JOIN 形態(避免 filesort):
SELECT m2.* FROM t m1, t m2 WHERE m1.id = m2.id AND m1.column1 <= 100 AND (m1.id < 500 OR m1.column1 < 100) ORDER BY m1.column1 DESC, m1.id DESC LIMIT 10;
| 方案 | 深分頁耗時 | 適用場景 | 備註 |
|---|---|---|---|
| 原生 LIMIT offset, N | 37 秒 | 僅適合淺分頁 | 基線 |
| INNER JOIN 延遲關聯 | 0.2 秒 | 帶 WHERE / 複雜排序 | 通用首選 |
| 主鍵範圍查詢 (id > last_id) | 0.001 秒 | 無 WHERE、按主鍵序 | 性能最強,需業務配合 |
| 唯一邊界值 (column + pk) | 0.002 秒 | 非唯一索引排序 | 解決重複值翻頁抖動 |
last_id 或 last_sort_key,而非 page_no。search_after 原生支援深分頁。MySQL 高性能分頁核心思想:**用索引定位邊界,替代 OFFSET 掃描丟棄**。按業務場景分層選擇:
① 無條件主鍵序 → id > last_id 範圍查詢(最快)
② 非唯一排序 → sort_col + pk 唯一邊界值
③ 複雜條件 → INNER JOIN (SELECT pk ...) 延遲關聯(通用兜底)
徹底杜絕 LIMIT 1000000, 10 這類寫法進入生產環境。