延迟关联通过先查ID再回表优化深分页,需覆盖索引、唯一JOIN字段及正确索引设计;游标分页更轻量但仅支持顺序翻页,二者互补而非替代。

延迟关联能绕过MySQL的OFFSET硬伤
MySQL执行LIMIT 1000000, 10时,不是“跳到第1000000行再取”,而是真的一行行扫描、计数、丢弃前1000000行——哪怕只用主键索引,也要走完1000010个索引节点。延迟关联把这一步拆开:SELECT id FROM ... ORDER BY ... LIMIT 1000000, 10只读索引页,不碰数据页;外层再用这10个id精确回表。I/O量从百万级降到个位数。
子查询必须走覆盖索引,否则优化失效
常见错误是子查询里ORDER BY created_at但没建(status, created_at, id)这样的联合索引。结果EXPLAIN显示Using filesort或type=ALL,子查询本身变慢,整个优化反而更差。关键点:
-
WHERE条件字段必须是联合索引最左前缀 -
ORDER BY字段要能复用同一索引的有序性(比如(sex, rating)支持WHERE sex='M' ORDER BY rating) - 末尾必须带上
id(或主键),否则无法覆盖
JOIN比IN更可靠,但字段必须唯一
不能写WHERE id IN (SELECT id FROM ... LIMIT ...)——MySQL禁止子查询带LIMIT的IN用法。必须用INNER JOIN。但要注意:ON o.id = tmp.id这步依赖id是主键或有唯一索引,否则JOIN可能退化成嵌套循环,性能崩塌。如果表没主键,得确保子查询返回的字段有唯一约束+索引。
游标分页比延迟关联更轻量,但场景受限
如果业务只支持“下一页”,直接用WHERE id > 1234567 ORDER BY id LIMIT 10就行,完全避开LIMIT offset。但游标值必须来自上一页**实际返回的最后一行id**,不能靠“页码 × 每页条数”算——因为删改会导致ID不连续。延迟关联适合后台系统允许跳页的场景,而游标更适合前端无限滚动。两者不是替代关系,是互补策略。
JOIN会返回空集,而不是自动截断。业务层得自己判断是否到底部,不能指望SQL自动兜底。


















