LIMIT offset, rows 語法要求 MySQL:
offset 行 (需掃描前 offset 行)rows 行隨著 offset 增大,掃描成本線性增長,百萬偏移 = 百萬行掃描,即使有索引也需遍歷 B+樹葉子節點鏈表。
| 項目 | 配置 |
|---|---|
| OS | Debian Linux |
| MySQL | 5.0.24 |
| 表引擎 | MyISAM |
| 表大小 | > 80MB, > 50,000 行, > 25 列 |
| 索引 | 主鍵 id,列 leibie 有索引 | Query Cache | 關閉 |
| Offset 位置 | 原始 SQL 耗時 | 優化 SQL 耗時 | 提升倍數 |
|---|---|---|---|
| 100 | 0.0055s | 0.0112s | 0.5x (優化反而慢) |
| 300 | 0.0067s | 0.0055s | 1.2x |
| 500 | 0.0062s | 0.0037s | 1.7x |
| 1,000 | 0.0113s | 0.0043s | 2.6x |
| 2,590 | 0.0140s | 0.0050s | 2.8x |
| 5,290 | 0.0245s | 0.0065s | 3.8x |
| 10,290 | 0.0707s | 0.0087s | 8.1x |
| 20,290 | 0.1070s | 0.0191s | 5.6x |
| 30,290 | 0.1527s | 0.0208s | 7.3x |
| 50,290 | 0.2647s | 0.0377s | 7.0x |
結論: Offset < 100 時優化無優勢甚至更慢;Offset > 100 顯著提升,**Offset 越大優化越明顯**,5萬偏移優化 **7 倍**。
SELECT * FROM testtable LIMIT 50290, 10;
SELECT * FROM testtable
WHERE id >= (SELECT id FROM testtable LIMIT 50290, 1)
LIMIT 10;
原理:
SELECT id FROM testtable LIMIT 50290, 1 僅掃描主鍵索引 (極窄),定位目標 ID,成本極低WHERE id >= target_id LIMIT 10 走主鍵範圍查詢 (type=range),直接定位葉子節點,無全掃描| Offset | 原始 SQL | 優化 SQL | 結果 |
|---|---|---|---|
| 990 | 0.0086s | 0.0123s | 原始略優 |
| 2,990 | 0.0502s | 0.0357s | 優化 1.4x |
| 5,690 | 0.0547s | 0.2101s | 優化反而慢 3.8x |
原因分析: 子查詢 SELECT id FROM testtable WHERE leibie=1 LIMIT 5690, 1 仍需掃描二級索引 5690 行,且外層 WHERE id >= ... AND leibie=1 可能導致回表或索引合併失效,優化器選擇不佳。
需要深分頁?
├─ 否 (Offset < 100) → 直接 LIMIT offset, rows
└─ 是 → 是否有高選擇性 WHERE 條件?
├─ 無 (全表分頁) → 延遲關聯優化 (主鍵遞增) / 游標分頁
└─ 有 → WHERE 列有覆蓋索引?
├─ 是 → 延遲關聯優化 (子查詢走覆蓋索引)
└─ 否 → 游標分頁 (記住上頁最大 ID) / 應用層緩存 / 搜索引擎
| 方案 | 適用場景 | 核心原理 | 優點 | 缺點 |
|---|---|---|---|---|
| 延遲關聯 (子查詢) | 無 WHERE / WHERE 列有覆蓋索引 / 主鍵遞增 | 子查詢走窄索引定位 ID,外層主鍵範圍查詢 | SQL 改動小、兼容性好 | WHERE 無覆蓋索引時可能更慢 |
| 游標分頁 (Keyset Pagination) | 所有場景,尤其 APP 無限滾動、API 分頁 | 記住上頁最大主鍵:WHERE id > last_id LIMIT 10 | 極快、穩定、無 OFFSET | 不支持跳頁、需前端配合、排序列必須唯一 |
| 應用層緩存 (Redis) | 熱門列表、首頁推薦、排行榜 | 全量數據緩存至 Redis List/Sorted Set,分頁在內存完成 | 毫秒級、零 DB 壓力 | 數據實時性弱、內存成本、一致性維護 |
| 搜索引擎 | 複雜條件、全文搜索、多維篩選 | ES/Solr 倒排索引天然支持深分頁 (scroll/search_after) | 功能強、性能優 | 架構複雜、數據同步延遲、運維成本高 |
-- 第一頁
SELECT * FROM articles WHERE status=1 ORDER BY id DESC LIMIT 10;
-- 記住最後一行 id = 9990
-- 下一頁 (無 OFFSET,極快)
SELECT * FROM articles WHERE status=1 AND id < 9990 ORDER BY id DESC LIMIT 10;
-- 通用模板 (支持任意排序列,需唯一)
SELECT * FROM tbl
WHERE sort_col > last_sort_val -- 或 < 視排序方向
[AND tie_breaker_col > last_tie_val] -- 排序列非唯一時需加斷鍵列
ORDER BY sort_col [, tie_breaker_col]
LIMIT 10;
EXPLAIN 驗證EXPLAIN 估算或單獨統計表LIMIT 深分頁優化的本質是**「避免掃描無用行」**。游標分頁 是架構級最優解:無 OFFSET、穩定毫秒級、天然適配無限滾動。延遲關聯 是存量 SQL 改造最低成本方案,但受限於優化器選擇。應用層緩存/搜索引擎 是高併發熱點數據的終極歸宿。**選型原則:業務允許 → 游標分頁;存量改造 → 延遲關聯驗證後上線;熱點列表 → Redis 緩存;複雜檢索 → ES。** 記住:Offset 是惡魔,Keyset 是天使。