mysql深分页越查越慢,是因为limit offset, count需顺序扫描并丢弃前offset行,无法真正跳转;offset越大,扫描行数越多,i/o和cpu开销线性增长,尤其无索引或回表时性能断崖式下降。

为什么 LIMIT offset, count 会越查越慢?
当 offset 很大时(比如 LIMIT 1000000, 20),MySQL 仍需扫描前 100 万行才能跳到目标位置,实际执行的是“全表扫描 + 跳过”,不是真正意义上的“跳转”。尤其在无合适索引或 WHERE 条件未命中索引时,性能断崖式下降。
- 用
EXPLAIN查看执行计划,如果rows值远大于你想要的count,说明存在扫描浪费 - 避免
ORDER BY RAND()配合大 offset,这会让优化器完全放弃索引 - 主键自增且连续时,可用
WHERE id > last_seen_id LIMIT 20替代 offset 方式(游标分页)
如何写安全的分页 SQL?
直接拼接用户传入的 page 和 size 参数极易引发 SQL 注入或越界错误。必须做校验和类型转换。
-
page必须 ≥ 1,size建议限制在 1–100 之间(可配置),超出则截断或报错 - 计算
offset = (page - 1) * size后,应检查是否超过SELECT COUNT(*)总数,避免返回空结果却消耗资源 - 不要用字符串拼接构建 SQL,优先使用预处理语句:
SELECT * FROM users WHERE status = ? ORDER BY id DESC LIMIT ?, ?
OFFSET 分页和游标分页怎么选?
OFFSET 分页适合前端需要“跳转任意页”的场景(如后台管理页码输入框);游标分页(基于排序字段值)更适合无限滚动、Feed 流等只向前/向后翻的场景。
- 游标分页依赖稳定、唯一、有索引的排序字段(如
id或created_at, id复合索引) - 游标查询示例:
SELECT * FROM posts WHERE created_at - 游标方式无法直接跳转第 N 页,但响应快、一致性好,且天然规避了“新数据插入导致页偏移”的问题
ORDER BY 字段没索引会导致 LIMIT 失效吗?
不会“失效”,但会让 LIMIT 前的排序变成 filesort,严重拖慢查询。MySQL 必须先完成全部排序,再取前 N 行——即使你只想要 20 条。
- 执行
EXPLAIN,若Extra列出现Using filesort,说明排序未走索引 - 复合索引顺序要匹配
ORDER BY:例如ORDER BY status, created_at DESC,索引应建为(status, created_at) - 注意 NULL 值影响:默认
ORDER BY xxx DESC中 NULL 排最前,可能打乱预期顺序,必要时加IS NOT NULL过滤
游标分页看似多一步维护 last_id,但线上高并发列表页几乎都这么干——不是因为它更“高级”,而是 offset 在百万级数据下真的扛不住。别等慢了才想起加索引,得在写第一条分页 SQL 时就考虑排序字段的索引覆盖。











