思路换成"记住上一页最后一条的主键或排序值",下一页直接 WHERE id > last_id ORDER BY id LIMIT 20。数据库只要从上次的位置往后顺序扫 20 行,不再扫描丢弃;无论翻到第几页,耗时都和第一页几乎一样。代价是不支持"跳到第 N 页",只能"上一页/下一页",但这正好契合绝大多数后台场景(用户从来不会真去点第 5000 页,只是下拉加载更多)。
3. 延迟关联(Deferred Join)救场"必须按非主键排序"的场景
如果列表必须按"最新时间"或"得分"排序,而排序键不是主键,纯游标分页就不好套。这时用子查询先只取主键,再用主键回原表拿完整行:先 SELECT id FROM t ORDER BY score DESC LIMIT 20 OFFSET 1000000 拿到 20 个 id,再 SELECT * FROM t WHERE id IN (...)。因为子查询只走覆盖索引、不回表,丢弃百万行的成本大幅下降;第二步用主键等值查,毫秒级。
4. 让索引"包住"排序和过滤
分页慢常源于"排序没走索引被迫 filesort"。给 ORDER BY + WHERE 的组合建联合索引(排序键放在过滤键之后),让排序在索引内完成;再配合覆盖列,避免回表。关键是让执行计划里的 Extra 出现 Using index,而不是 Using filesort 或 Using temporary。