OFFSET越大查询越慢,因为MySQL需扫描前N行才能定位结果起始位置,无法跳过中间数据,导致I/O和CPU开销剧增;延迟关联和ID范围过滤可有效优化。

为什么 OFFSET 越大,LIMIT 查询越慢
因为 MySQL 在执行 SELECT * FROM orders LIMIT 1000000, 20 时,仍要扫描前 1000000 行才能定位到结果起始位置——即使只取 20 条。InnoDB 的聚簇索引顺序扫描无法跳过中间数据,磁盘 I/O 和 CPU 解析开销直线上升。
常见错误现象:EXPLAIN 显示 rows 高达百万级,但 Extra 字段里没有 Using index;查询耗时从几十毫秒暴涨到数秒甚至超时。
- 不是数据量大才慢,是偏移量(OFFSET)大才慢
- 覆盖索引能缓解但不治本:即便
SELECT id, created_at走了联合索引,OFFSET 仍需跳过百万索引项 - ORDER BY 非主键字段(如
ORDER BY status, created_at)会让优化器放弃索引下推,直接回表+文件排序
用延迟关联(Deferred Join)把分页变成“查ID再回表”
核心思路:先用最小代价查出需要的主键 id,再用这些 id 精确回表。避免全字段扫描和 OFFSET 跳行。
实操写法:
SELECT o.* FROM orders o
INNER JOIN (
SELECT id FROM orders
WHERE status = 1
ORDER BY created_at DESC
LIMIT 1000000, 20
) AS tmp ON o.id = tmp.id;
关键点:
- 子查询只查
id,且WHERE + ORDER BY必须能命中索引(例如(status, created_at)联合索引) - 外层
JOIN是等值匹配,走主键索引快速回表,实际只读 20 行完整记录 - 如果原表没主键或
id不是主键,必须确保子查询结果集中的字段是唯一且有索引的
用 ID 范围过滤替代 OFFSET(推荐用于时间/自增主键场景)
适用于有单调递增字段(如 id 或 created_at)且业务允许“游标式”翻页的场景。本质是把“第 N 页”转成“大于上一页最后一条的 ID”。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
示例(按 id 倒序分页):
SELECT * FROM orders WHERE id <p>注意事项:</p>
- 必须有索引支撑
WHERE id ,否则退化为全表扫描;id主键天然满足 - 不能跳页(比如直接跳到第 1000 页),只能逐页推进;适合 Feed 流、日志查看等场景
- 若排序字段是
created_at,需注意精度问题:同一毫秒内多条记录会导致漏数据或重复,建议组合(created_at, id)作为游标
别忽略 WHERE 条件对分页性能的决定性影响
很多同学只盯着 LIMIT,却忘了 WHERE 没走索引才是根本瓶颈。延迟关联和 ID 过滤都依赖前置条件能高效收敛数据集。
检查步骤:
- 用
EXPLAIN看type是否为range或ref,key是否命中预期索引 - 避免在
WHERE中对字段做函数操作,例如WHERE DATE(created_at) = '2024-01-01'会失效索引 - 复合查询中,
ORDER BY字段必须包含在索引最右位置,否则可能触发Using filesort - 如果业务允许,把高频分页字段(如
status)加进联合索引前缀,比单独建索引更有效
真正卡住千万级分页的,往往不是 LIMIT 本身,而是前面那句 WHERE 没筛掉几行数据,导致后续所有优化都建立在沙丘之上。










