LIMIT 1000000, 20 會慢?MySQL 需掃描前 1,000,000 行再丟棄,再取 20 行。隨著偏移量增大,耗時線性增長。
-- 假設 id 為主鍵/唯一索引,每頁 20 條
SELECT * FROM `t`
WHERE `id` > 1000000 -- 上一頁最大 id
ORDER BY `id` ASC
LIMIT 20;
type=range,無 filesort,毫秒級返回。-- 子查詢法
SELECT * FROM `t`
WHERE `id` <= (
SELECT `id` FROM `t` ORDER BY `id` DESC LIMIT 1000000, 1
)
ORDER BY `id` DESC LIMIT 20;
-- 聯接法(通常更快)
SELECT a.* FROM `t` AS a
JOIN (
SELECT `id` FROM `t` ORDER BY `id` DESC LIMIT 1000000, 1
) AS b ON a.`id` <= b.`id`
ORDER BY a.`id` DESC LIMIT 20;
SELECT a.* FROM `t` AS a
JOIN (
SELECT `id` FROM `t` ORDER BY `id` LIMIT 1000000, 20
) AS b USING (`id`);
Using index),外層按主鍵精確回表 20 行。-- 只查索引覆蓋的列
SELECT `id`, `title`, `created_at` FROM `articles`
WHERE `status` = 1
ORDER BY `created_at` DESC
LIMIT 1000000, 20;
PREPARE stmt FROM
'SELECT * FROM `t` WHERE `id` > ? ORDER BY `id` ASC LIMIT ?';
SET @last_id = 1000000, @page_size = 20;
EXECUTE stmt USING @last_id, @page_size;
CREATE PROCEDURE `sp_page`(IN last_id BIGINT, IN page_size INT)
BEGIN
SELECT * FROM `t` WHERE `id` > last_id ORDER BY `id` LIMIT page_size;
END;
預先計算分頁切片 ID 範圍存入輔助表,查詢時直接 JOIN。
SQL_CALC_FOUND_ROWSSELECT SQL_CALC_FOUND_ROWS * FROM `t` LIMIT 1000000, 20;
SELECT FOUND_ROWS();
是否支援「游標式」(前端可傳 last_id)?
├─ 是 → 方案 1(最優)
└─ 必須支援「跳頁」
├─ 偏移量 < 10,000 → 方案 4(覆蓋索引)或原生 LIMIT
├─ 偏移量 10,000~1,000,000 → 方案 3(延遲關聯)
└─ 偏移量 > 1,000,000 → 方案 2(子查詢/聯接定位)或方案 7(物化表)
| 方案 | LIMIT 10000,20 | LIMIT 1000000,20 | LIMIT 5000000,20 |
|---|---|---|---|
| 原生 LIMIT | 12 ms | 1,200 ms | 6,500 ms |
| 游標式 (id > last) | 2 ms | 2 ms | 2 ms |
| 延遲關聯 | 8 ms | 15 ms | 30 ms |
| 子查詢定位 | 10 ms | 25 ms | 45 ms |