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

MySQL 大數據量分頁優化:避免 OFFSET 深分頁性能崩潰

     編輯:MySQL 資料庫相關知識

MySQL LIMIT offset, rows 在深分頁(大 offset)時需掃描並丟棄 offset 行,導致性能隨頁碼線性惡化。本文介紹基於覆蓋索引+延遲關聯、游標分頁、記憶上一頁最大 ID 三種優化方案。

一、問題現象

-- 第 10 萬頁,每頁 20 行
SELECT * FROM huge_table ORDER BY id LIMIT 2000000, 20;
-- 執行計劃:type=ALL / index,rows≈200 萬,需讀取並丟棄 200 萬行

二、方案對比

方案原理適用場景優點缺點
覆蓋索引+延遲關聯先只查主鍵 SELECT id FROM t ORDER BY id LIMIT offset, n,再 JOIN 回表取列單表、有序主鍵、需任意跳頁減少回表 IO,兼容任意頁碼仍需掃描 offset 行索引
游標/Keyset 分頁記住上一頁最大 ID,WHERE id > last_id ORDER BY id LIMIT n單向瀏覽(列表、Feed、API 分頁)O(1) 性能,無 offset 掃描不支持隨機跳頁、需唯一有序鍵
記憶上一頁最大值(應用層)前端傳遞 after_id 參數無限滾動、移動端列表極簡實現、極致性能同上

三、最佳實踐:延遲關聯 SQL 模板

-- 原始慢查詢
SELECT * FROM orders WHERE status=1 ORDER BY created_at DESC LIMIT 100000, 20;

-- 優化:覆蓋索引 (status, created_at, id) + 延遲關聯
SELECT o.*
FROM orders o
JOIN (
  SELECT id
  FROM orders
  WHERE status=1
  ORDER BY created_at DESC
  LIMIT 100000, 20
) tmp USING (id);

子查詢僅掃描索引(Using index),外層按主鍵精確回表 20 行。

四、游標分頁實戰(推薦用於 API/Feed)

第一頁

SELECT id, title, created_at
FROM articles
WHERE status=1
ORDER BY id DESC
LIMIT 20;

後續頁(前端傳遞 last_id)

SELECT id, title, created_at
FROM articles
WHERE status=1 AND id < 987654321  -- last_id
ORDER BY id DESC
LIMIT 20;

索引 (status, id) 即可完美支持,EXPLAIN 顯示 range、rows=20。

五、總數統計優化

大表 COUNT(*) 同樣昂貴,建議:

  • 近似值:EXPLAIN SELECT COUNT(*) ... 讀取 rows 估算
  • 緩存:Redis 緩存總數,異步更新(每小時/每天)
  • 觸發器維護計數表:INSERT/DELETE 時 UPDATE stats SET cnt=cnt±1
  • 業務允許時:不提供總頁數,僅「下一頁」按鈕(游標分頁天然適配)

六、避坑提醒

  • 不要在 WHERE 對列使用函數(DATE(created_at)),導致索引失效
  • 複雜 JOIN 分頁:先在駆動表分頁取主鍵,再關聯其它表
  • ORDER BY 非索引列:會觸發 filesort,配合 LIMIT 仍需全量排序
  • MySQL 8.0 支持 LIMIT ... OFFSET ... 語法糖,底層機制未變

深分頁優化核心思想:**減少掃描行數、利用索引順序、業務接受游標式分頁**。根據業務場景選擇方案,能帶來數量級性能提升。

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