EXPLAIN SELECT * FROM message ORDER BY id DESC LIMIT 100000, 20;
-- type=index, rows=100020 → 掃描 10 萬行丟棄 10 萬,極慢
LIMIT offset, rows 會掃描 offset + rows 行,**offset 越大越慢**,高併發下直接拖垮資料庫。
-- 1. 先用覆蓋索引快速定位主鍵
SELECT id FROM message ORDER BY id DESC LIMIT 100000, 20;
-- 2. 再回表查詢完整列
SELECT * FROM message
WHERE id IN (上述子查詢)
ORDER BY id DESC; -- 保持順序
或單條 SQL (衍生表):
SELECT m.*
FROM message m
INNER JOIN (
SELECT id FROM message ORDER BY id DESC LIMIT 100000, 20
) AS t USING (id)
ORDER BY m.id DESC;
offset + rows → 仍隨 offset 增長,但**不回表大字段**,I/O 大幅降低。-- 首頁
SELECT * FROM message ORDER BY id DESC LIMIT 20;
-- 下一頁 (客戶端記住上一頁最小 id = 1000)
SELECT * FROM message WHERE id < 1000 ORDER BY id DESC LIMIT 20;
-- 上一頁 (記住上一頁最大 id = 1020)
SELECT * FROM message WHERE id > 1020 ORDER BY id ASC LIMIT 20; -- 取完再反轉
id < 1000),極快。若業務**必須**顯示「1 2 3 ... 10」頁碼跳轉:
-- 當前第 10 頁,id 範圍 2500~2519 (每頁 20)
-- 跳第 9 頁 (前一頁)
SELECT * FROM message WHERE id < 2500 ORDER BY id DESC LIMIT 20;
-- 跳第 8 頁 (前兩頁)
SELECT * FROM message WHERE id < 2500 ORDER BY id DESC LIMIT 20 OFFSET 20;
-- 跳第 11 頁 (後一頁)
SELECT * FROM message WHERE id > 2519 ORDER BY id ASC LIMIT 20; -- 取完應用層反轉
-- 跳第 12 頁
SELECT * FROM message WHERE id > 2519 ORDER BY id ASC LIMIT 20 OFFSET 20;
page_id, item_id, sort_key)。JOIN 物化表,極快。| 方案 | 第 1 萬頁 (offset=200000) 耗時 | 備註 |
|---|---|---|
| 原生 LIMIT 200000, 20 | ~1.2s | 全表掃描 + filesort |
| 延遲關聯 (覆蓋索引) | ~0.15s | 索引掃描 + 20 次回表 |
| 游標分頁 (id < ...) | ~0.002s | 索引範圍查詢,極快 |
「用索引定位,不掃描垃圾行;用游標替代偏移,讓分頁效能與資料量無關。」
根據業務交互模式選擇方案,徹底解決 MySQL 深分頁效能難題。