延迟关联是优化mysql超大位移量查询最实用方案,通过先查主键再join回表,避免扫描丢弃大量数据,性能提升90%+;需确保排序字段有索引、不使用索引失效函数、主键类型合理。

Java 中优化 MySQL 超大位移量(比如 LIMIT 1000000, 20)的查询,核心不是改 Java 代码本身,而是把 SQL 写对——延迟关联(Deferred Join)就是最实用、落地最稳的方案。它不依赖前端页码跳转逻辑变更,适合后台管理类系统。
延迟关联为什么能快 90%+
原始写法:SELECT * FROM orders WHERE status = 1 ORDER BY id DESC LIMIT 1000000, 20,MySQL 必须扫描并丢弃前 1000000 行,再取 20 行,回表成本极高。
延迟关联拆成两步:
– 第一步只查主键(id),走覆盖索引,不回表,毫秒级完成;
– 第二步用这 20 个 id 精准回查原表,避免全行扫描。
本质是把“大海捞针”变成“按地址上门取货”,I/O 和 CPU 压力大幅下降。
Java 中怎么写这个 SQL(MyBatis 示例)
不要在 Mapper XML 或注解里写 SELECT * 直接带 OFFSET,而是显式写出两层 JOIN:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
<select id="selectOrdersByPage" resulttype="Order">
SELECT o.*
FROM orders o
INNER JOIN (
SELECT id
FROM orders
WHERE status = #{status}
ORDER BY id DESC
LIMIT #{offset}, #{size}
) tmp ON o.id = tmp.id
</select>
关键点:
– 子查询中只选 id,且 ORDER BY 字段(这里是 id)必须有索引;
– 外层 ON o.id = tmp.id 能命中主键索引,JOIN 效率高;
– #{offset} 和 #{size} 仍由 Java 传入,无需改造分页参数逻辑。
必须配套的数据库准备
延迟关联不是“银弹”,它高度依赖底层索引设计:
-
排序字段必须建索引:比如
ORDER BY id DESC,id是主键天然满足;若按created_at排序,就得单独建INDEX idx_created_at (created_at) -
避免函数破坏索引:别写
ORDER BY DATE(created_at),否则子查询无法走索引,延迟关联就失效 - 主键类型要合理:优先用自增整型;如果用 UUID,B+ 树分裂频繁,范围扫描变慢,延迟关联收益会打折扣
- 宽表场景更受益:表里有 TEXT、JSON 或大量 VARCHAR 字段时,延迟关联省下的回表 I/O 更明显
什么时候该换游标分页
如果业务允许(比如 feed 流、日志列表、仅支持“下一页”),建议直接迁移到游标分页,性能更稳:
- 上一页最后一条记录
id = 6800000,下一页查:SELECT * FROM orders WHERE id > 6800000 ORDER BY id LIMIT 20 - Java 侧只需传
lastId而非page,无 OFFSET 扫描,响应稳定在几毫秒 - 时间字段分页需防重复:
WHERE updated_at
延迟关联是“兼容旧逻辑”的务实选择,游标分页是“面向未来”的最优路径。两者都绕不开索引——没索引,再好的写法也白搭。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










