P32 深分页为什么慢,怎么优化?
面试题:LIMIT 1000000, 10 这种深分页为什么慢?怎么优化?
1. 为什么深分页慢
sql
SELECT * FROM t ORDER BY id LIMIT 1000000, 10;执行过程:
- 数据库要扫描并丢弃前 100 万行(虽然只返回 10 条);
- 如果有排序/where,还要先排序再跳过;
LIMIT offset, size的 offset 越大,扫描的行越多,耗时线性增长。
本质:offset 越大,白白扫描的行越多,而 MySQL 没有"直接跳到第 100 万行"的能力。
2. 优化方案
① 延迟关联(子查询先定位,再回表取整行)
sql
SELECT t.* FROM t
JOIN (SELECT id FROM t ORDER BY id LIMIT 1000000, 10) tmp
ON t.id = tmp.id;子查询只查主键(覆盖索引,扫描代价小),再按主键回表取 10 行。
② 基于游标/上一页位置(书签法,推荐)
用上一页的最后一条记录作为过滤条件,而不是 offset:
sql
-- 第一页:LIMIT 10
SELECT * FROM t ORDER BY id LIMIT 10;
-- 第二页:id > 上一页最后一条
SELECT * FROM t WHERE id > 1000 ORDER BY id LIMIT 10;每次只扫 10 行,深度无限也不慢。要求排序字段唯一且有序(如自增主键)。
③ 覆盖索引 + 延迟关联组合
SELECT 主键 走覆盖索引扫,再 join 回表。
④ 禁止深分页
- 搜索引擎式分页(跳页)本质不适用于大 offset,限制"只能翻前 N 页",之后用"加载更多";
- 业务上引导用条件筛选、时间范围,减少总数据量。
3. 加分点
- 对比"为什么
WHERE id > ?快":B+ 树直接定位,不需要丢弃前 N 行; - 无唯一有序字段时,可以人为加自增 id 或连续号段;
- 追问"count 也慢":深分页通常还要 count 总页数,大表 count 也要优化(见 P29)。
一句话总结
深分页慢是因为 OFFSET 要扫描并丢弃大量行;优化用延迟关联(先取主键再回表)或游标式翻页(WHERE id > 上页最大值),让每次查询只扫需要的行。