跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

如何在MySQL中利用子查询优化大分页查询_通过延迟关联技术减少IO

LIMIT 100000, 20 极慢是因为 MySQL 必须从头扫描并回表 100020 次,丢弃前 100000 行,触发大量随机 IO;应改用延迟关联或游标分页。 为什么
LIMIT 100000, 20
会慢得离谱 MySQL 执行
LIMIT offset, size
时,并不是直接跳到第
offset
行再取数据,而是从头扫描,逐行计数,直到累计读够
offset + size
行,再丢掉前
offset
行。当
offset = 100000
,它就得回表 100020 次——哪怕你只想要最后 20 条。二级索引查 ID、再用 ID 回主键索引取整行,每一步都在触发随机 IO,缓存命中率暴跌,性能断崖式下滑。
SELECT * FROM t JOIN (SELECT id FROM t ... LIMIT N,M) AS tmp ON t.id = tmp.id
怎么写才真正生效 延迟关联的核心是“先轻后重”:子查询只走索引取
id
,外层再按主键精准拉数据。但必须满足几个硬条件,否则照样慢: 子查询的
ORDER BY
字段必须有**覆盖索引**(比如
ORDER BY create_time DESC, id DESC
,对应联合索引
KEY idx_ct_id (create_time, id)
),否则子查询本身就会全表扫描 外层
JOIN
的关联字段必须是主键或唯一键,否则 MySQL 可能放弃使用
eq_ref
访问类型,退化为
ALL
或
range
避免在子查询里
SELECT *
或带非索引字段,否则无法触发
Using index
,EXPLAIN 的
Extra
列会显示
Using where; Using filesort
示例正确写法:
SELECT a.* FROM articles a INNER JOIN ( SELECT id FROM articles WHERE status = 1 ORDER BY created_at DESC, id DESC LIMIT 100000, 20 ) AS tmp ON a.id = tmp.id;
和
WHERE id >= (SELECT id FROM ... LIMIT N,1)
对比,哪种更适合你的场景 两者都绕开了大 offset,但适用边界很不同: MySQL(Linux) MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。 下载
WHERE id >= (SELECT ... LIMIT N,1)
要求排序字段与主键强相关(如自增
id
),且数据不能大量删除,否则页码错位;适合简单主键分页,但不支持多条件过滤 + 复合排序(比如
ORDER BY category, updated_at
) 延迟关联对排序字段无强依赖,只要该排序能走索引(哪怕是联合索引),就能稳定生效;支持任意
WHERE
条件 +
ORDER BY
组合,业务适配性更强 性能上,延迟关联在
offset > 50w
时通常更稳;而子查询取单个起始
id
的方式,在高并发下可能因子查询被重复执行多次带来额外开销 注意:如果业务允许“只能下一页”,优先用游标式(
WHERE created_at < ? AND id < ?
),它比两种方案都快一个数量级 容易被忽略的三个坑 延迟关联不是套个
JOIN
就万事大吉,真实线上踩过这些坑的人才知道: 没加
STRAIGHT_JOIN
时,MySQL 优化器可能把外层表当驱动表,导致全表扫描;强制指定顺序:
SELECT STRAIGHT_JOIN a.* FROM (SELECT id ...) AS tmp JOIN articles a ON a.id = tmp.id
子查询结果集若包含重复
id
(比如
GROUP BY
或
DISTINCT
没处理好),外层
JOIN
会放大结果行数,甚至翻倍 如果分页字段存在大量相同值(如千万条记录
status = 1
),
ORDER BY status, id
中的
status
区分度太低,B+ 树遍历时仍要比较大量
id
,此时必须补上足够区分的第二排序字段,否则延迟关联优势大幅缩水 实际效果取决于索引设计是否真正覆盖了子查询路径,而不是 SQL 表面上用了
JOIN
。很多团队测完发现没提速,问题八成出在子查询那一步根本没走索引。

相关文章