mysql的limit不是函数而是语法糖,执行时需先排序再扫描丢弃前offset行,导致深度分页变慢;若order by无有效索引,则触发filesort,加剧性能问题。

MySQL的LIMIT不是函数,但它的执行逻辑导致深度分页变慢
很多人误以为LIMIT是个“高效函数”,其实它只是语法糖。MySQL执行LIMIT offset, size时,并不会跳到第offset + 1行直接读,而是:先按ORDER BY完整排序所有匹配行 → 扫描前offset + size条 → 丢弃前offset条 → 返回剩下size条。数据量越大,丢弃成本越高。
ORDER BY字段没索引或索引失效,LIMIT就彻底失去意义
即使OFFSET很小,只要ORDER BY列没走索引,MySQL就会触发Using filesort——在内存或磁盘中对全量结果排序,千万级数据下极易OOM或写临时文件。常见索引失效场景包括:
-
WHERE条件用了函数或隐式转换,比如WHERE DATE(create_time) = '2025-01-01'(应改用范围:create_time >= '2025-01-01' AND create_time ) - 复合索引顺序不匹配查询模式,例如建了
(status, id),却执行ORDER BY id且WHERE status = ? - 模糊查询带前置
%:LIKE '%关键词'无法利用索引,导致排序前就得全表扫描
用JOIN子查询做延迟关联,能绕过LIMIT的丢弃陷阱
核心思路是把“排序+截断”和“查字段”拆开:先用覆盖索引快速拿到目标id列表,再用这些id精准回表。这样避免了大偏移量下反复加载无用字段。
- 必须确保子查询只查
id(或主键),且ORDER BY字段有索引 - 外层
JOIN比IN更可靠:MySQL早期版本不支持子查询带LIMIT,而IN (SELECT ... LIMIT)在5.7+才被优化,但仍有风险 - 示例写法:
SELECT t.* FROM orders t JOIN (SELECT id FROM orders WHERE status = 1 ORDER BY id DESC LIMIT 1000000, 20) tmp ON t.id = tmp.id
游标分页不是“替代方案”,而是对LIMIT根本缺陷的正向规避
它不依赖OFFSET,所以完全避开“丢弃N行”这个性能黑洞。但要求严格:
- 游标字段必须单调、唯一、非空,推荐用自增
id或带毫秒精度的create_time -
WHERE条件必须和ORDER BY方向一致,比如ORDER BY id DESC就要用WHERE id ,不能混用<code>>和DESC - 不支持随机跳页(如直接点第87页),但可通过预查锚点ID间接实现,代价远低于直接
LIMIT 860000, 20
真正卡住性能的从来不是SQL写法本身,而是默认假设“数据库会聪明地跳过不需要的数据”——而MySQL在LIMIT这件事上,恰恰不会。











