MySQL 深分页为什么越来越慢?游标分页和延迟关联怎么优化?
🧑💻 面试官:MySQL 翻到第 5000 页越来越慢,给排序字段加个索引就能解决吗?
🙋♂️ 我:应该可以。索引能直接找到这一页的数据。
🧑💻 面试官:LIMIT 100000, 20 里的十万条,数据库真的不用经过吗?
🙋♂️ 我:那先只查 ID,再回表取这 20 条。
🧑💻 面试官:这减少了回表,可偏移扫描还在。要彻底绕开大偏移,你准备怎么改接口?
这道题要分清两笔开销:跳过多少条,以及为多少条读取完整记录。延迟关联减少后者,游标分页才有机会避开前者。
面试速答(60 秒版)
深分页慢,通常是因为数据库要找到有序结果,再跳过前面的很多条,只返回最后一小段。加索引能改善排序和读取,但不会让大 OFFSET 自动消失。
如果页面支持连续翻页,我会优先考虑游标分页:带上上一页最后一条的排序值和唯一 ID,从这个位置继续查。排序字段相同时,ID 负责确定先后。
如果必须跳到任意页,可以先通过覆盖索引查这一页的 ID,再关联主表取详情,减少前面那些记录的回表成本。不过它仍要经过偏移区间。
最后要用真实筛选条件和数据分布看执行计划。分页是否正确、是否需要固定快照,也要和性能一起考虑。

知识点详解:数据库为什么不能直接跳到第 5000 页
先把 OFFSET 理解成“跳过结果”,不是“数组下标”
假设后台按创建时间倒序展示任务,每页 20 条。LIMIT 100000, 20 的意思是:在符合条件的有序结果里,跳过十万条,再拿 20 条。
B+ 树能按键定位,但它通常没有“当前筛选条件下,第十万条就在某个地址”的目录。即使顺着排序索引读,也还需要经过前面的候选记录。没有合适索引时,还可能额外筛选和排序。
所以,面试里不能只说“深分页没有走索引”。已经走索引,也可能因为读过太多条而慢。
游标分页:下一页从上次的位置接着走
例如使用 (created_at, id) 建立索引,并约定两个字段都倒序。下一页的游标必须同时保存时间和 ID:
SELECT id, created_at, title
FROM tasks
WHERE created_at < :last_time
OR (created_at = :last_time AND id < :last_id)
ORDER BY created_at DESC, id DESC
LIMIT 20;
这里的冒号参数只是示意,由数据库驱动绑定,不要拼接用户输入。第一屏不带游标条件;上一屏最后一条就是下一屏的起点。实际还有租户、状态等过滤时,要重新设计联合索引并核对范围扫描,不能直接套用这个索引。
只保存时间是不够的。同一秒可能有很多任务,缺少唯一的第二排序字段,就无法明确从哪一条接着查。
延迟关联:先找小记录,再取大记录
页面非要展示“跳到第 5000 页”时,游标接口就不完全满足需求。这时可以让子查询先只选 ID 和排序字段,再关联主表取详情。
这样做的关键是:子查询尽量由索引覆盖,只有最终那 20 条才读取完整记录。外层仍应写明确的 ORDER BY,不能依赖子查询顺序自动保留。
它优化的是完整行读取量,不是把十万条偏移变成零。记录越宽、原查询回表越多,收益越可能明显;如果本来就只读覆盖索引,收益就不一定大。
翻页正确性也需要定规则
游标不是数据库快照。连续翻页期间,记录被删除、排序值改变,仍可能影响结果。新记录出现在游标之前,通常不会挤进后续页,但这不等于全程读到同一时刻的数据。
普通信息流可以接受这种变化;审计导出就可能需要固定查询范围或一致性快照。验证时应分别观察扫描条数、回表量、排序和耗时,并加入重复时间、删除和新增的测试。EXPLAIN ANALYZE 会实际执行查询,要在合适的测试环境使用。
面试官继续追问
游标能直接跳第 100 页吗?
通常不能。没有那一页的起点,就不能凭页号直接构造游标。产品要在连续翻页、任意跳页和查询成本之间做取舍。
按 ID 翻页一定正确吗?
只有 ID 顺序符合展示规则时才行。页面按更新时间排序,却用 ID 作为唯一游标,会改变用户看到的顺序。
有索引还慢,先看什么?
先看实际筛选比例、经过的记录量、回表和排序,再判断是不是索引设计问题,不先下结论说数据库没用索引。
面试速记卡
- OFFSET:跳过结果,不是直接定位数组下标。
- 游标:保存排序键和唯一键,沿位置继续查。
- 延迟关联:减少完整行读取,偏移成本仍在。
- 正确性:固定排序不等于固定快照。
- 验收:看扫描量、回表、排序和真实耗时。
公司面试真题
真题根据求职者公开面经整理,题意经过概括,非逐字原话或公司官方题库;本文为 Sunday 的独立解析。
京东 · Java后台 · 校招
大量数据怎样通过主键位置优化分页?(题意整理)
京东 Java 后台三面凉经 ↗
原帖编辑于 2019-08-23(历史校招面经)