Skip to content

P32 深分页为什么慢,怎么优化? ​

面试题:LIMIT 1000000, 10 这种深分页为什么慢?怎么优化?

1. 为什么深分页慢 ​

sql
SELECT * FROM t ORDER BY id LIMIT 1000000, 10;

执行过程:

  1. 数据库要扫描并丢弃前 100 万行(虽然只返回 10 条);
  2. 如果有排序/where,还要先排序再跳过;
  3. 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 > 上页最大值),让每次查询只扫需要的行。

基于 VitePress 重建