單表 2G+ 的 MyISAM 表執行 LIMIT 1275480, 20 導致查詢極慢,本文深入剖析 LIMIT 掃描機制,提出三種分頁優化策略與程式碼改寫實例。
LIMIT N, M 實際執行邏輯:先掃描 N+M 行,再捨棄前 N 行,取後 M 行。
當 N 達百萬級,等同全表掃描,I/O 與 CPU 開銷極大。
利用上一頁最後一筆的排序欄位值(如 postdate)作為下一頁的篩選條件:
SELECT * FROM pw_gbook WHERE uid='48' ORDER BY postdate DESC LIMIT 20;
SELECT * FROM pw_gbook WHERE uid='48' AND postdate < '2009-11-3' ORDER BY postdate DESC LIMIT 20;
SELECT * FROM pw_gbook WHERE uid='48' AND postdate > '2009-11-1' ORDER BY postdate DESC LIMIT 20;
優點:N 永遠為 0,索引範圍掃描極快。
限制:不支援任意跳頁。
從第 2 頁跳到第 5 頁(差 3 頁,每頁 20 筆):
SELECT * FROM pw_gbook WHERE uid='48' AND postdate < '2009-11-3' ORDER BY postdate DESC LIMIT 60, 20;
仍比原始 LIMIT 1275480, 20 好,但跳頁越遠 N 越大,效能遞減。大型網站因此限制跳頁範圍或不提供直接跳頁輸入框。
原查詢:
SELECT * FROM pw_gbook WHERE uid='48' ORDER BY postdate DESC LIMIT 1275480, 20;
改寫為:
SELECT * FROM pw_gbook WHERE id > 1275480 AND uid='48' ORDER BY postdate DESC LIMIT 20;
前提:id 為自增主鍵,且 id > 1275480 能精準定位到對應區間。此法將 OFFSET 掃描轉為索引範圍查找,效能提升數量級。
SELECT * FROM pw_gbook WHERE uid='48' ORDER BY postdate ASC LIMIT 20; 直接取最舊 20 筆。若業務允許,用 WHERE postdate BETWEEN '2009-11-01' AND '2009-11-30' 替代 LIMIT 分頁,配合索引效能極佳。