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

MySQL 千萬級數據分頁優化實戰:從 LIMIT 陷阱到複合索引破局

     編輯:MySQL 資料庫相關知識

前言:分頁查詢的性能噩夢

MySQL 的性能極限在哪裡?許多開發者在處理小型系統(萬級數據)時,隨便寫寫 SQL、套個框架就能跑得飛快。但當數據量衝擊到 10 萬、百萬甚至千萬級時,一條看似無害的 LIMIT offset, N 就能拖垮整個系統。筆者近期經歷了從痛苦、絕望到重拾信心的完整過程,決定用實測數據說話,帶大家徹底搞懂大數據分頁優化的核心邏輯。

現象還原:為什麼 LIMIT 越往後越慢?

測試表 collect (id, title, info, vtype),其中 id 為主鍵遞增,vtype 為 tinyint 並建立索引。填入 10 萬條記錄,表大小 1.6 GB。

  • SELECT id, title FROM collect LIMIT 1000, 10; → 0.01 秒,極快。
  • SELECT id, title FROM collect LIMIT 90000, 10; → 8-9 秒,災難級延遲。

同樣是掃描 10 條記錄,為什麼偏移量大就慢成這樣?MySQL 的 LIMIT offset, N 實際上會先掃描並丟棄前 offset 行,再取 N 行。偏移量越大,丟棄的行數越多,I/O 開銷呈線性增長。

嘗試 1:利用主鍵索引覆蓋查詢

SELECT id FROM collect ORDER BY id LIMIT 90000, 10;

結果:0.04 秒。因為只讀取主鍵索引(覆蓋索引),不回表,速度飛快。網上常見改寫方案:

SELECT id, title FROM collect 
WHERE id >= (SELECT id FROM collect ORDER BY id LIMIT 90000, 1) 
LIMIT 10;

此法利用主鍵定位起始 ID,再回表取 10 行,確實快。但問題來了——加上 WHERE vtype=1 後:

SELECT id FROM collect WHERE vtype=1 ORDER BY id LIMIT 90000, 10;

依然 8-9 秒!明明 vtype 有索引,為何失效?

嘗試 2:分表法(Discuz 思路)

建立索引表 t(id, title, vtype) 定長存儲,分頁查 t 再回 collect 取 info。10 萬數據時 t 表僅 20 MB,查詢 0.1-0.2 秒。但擴充到 100 萬(t 表 200+ MB),LIMIT 900000, 10 又飆升到 1-2 秒。分表並未根治問題,本質仍是全索引掃描,只是數據量小時快而已。

破局關鍵:複合索引 + 覆蓋查詢 + IN 子查詢

偶然發現:建立複合索引 search(vtype, id),WHERE 條件列在前,排序/分頁用的主鍵在後,且 只 SELECT 主鍵:

SELECT id FROM collect WHERE vtype=1 LIMIT 90000, 10;

驚人地快——0.04 秒!但一旦 SELECT id, title 就又回到 8-9 秒,因為觸發回表,不再走覆蓋索引。

核心結論:帶 WHERE 且需 LIMIT 分頁時,必須建立 (where_column, order_by_column) 複合索引,並僅查主鍵。

工程落地:兩步查詢法,完美兼容框架分頁

既然只能快速取 ID,那麼先查 ID 列表,再用 IN 批量回表:

// 步驟 1:分頁查主鍵(走覆蓋索引,極快)
$sql = "SELECT id FROM collect WHERE vtype=$vtype";
$db->pagesize = 20;
$db->execute($sql);
$strpage = $db->strpage(); // 保存分頁字符串

// 步驟 2:構造 ID 字符串,批量回表取完整字段
while ($rs = $db->fetch_array()) {
    $strid .= $rs['id'] . ',';
}
$strid = substr($strid, 0, -1);

$db->pagesize = 0; // 關鍵:清空分頁,復用連接
$db->execute("SELECT id, title, url, sTime, gTime, vtype, tag 
              FROM collect WHERE id IN ($strid)");
// 輸出模板渲染...

IN 列表同樣能命中主鍵索引,單次查詢 0 秒級。這招不需要分表、不改框架分頁邏輯,百萬級分頁穩定在 0.0x 秒。

總結

  1. LIMIT offset, N 深分頁本質是全掃描,offset 越大越慢。
  2. 覆蓋索引(只查索引列)能繞過回表,但加 WHERE 會失效。
  3. 複合索引 (where_col, order_col) + 只 SELECT 主鍵 是破局核心。
  4. 兩步查詢法(先分頁取 ID,再 IN 回表)工程落地成本最低,效果最佳。

MySQL 優化,索引設計是靈魂。掌握這套組合拳,千萬級分頁不再是夢。

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