MySQL LIMIT 深分页性能问题与优化实战
作者:Knight_AL
本文主要介绍了MySQL LIMIT 深分页性能问题与优化实战,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
一、题目回顾
执行语句如下:
SELECT * FROM orders LIMIT 100, 100;
意思是:
跳过前 100 行,取接下来的 100 行。
二、MySQL 的执行逻辑
MySQL 的 LIMIT offset, count 实际执行流程是这样的:
- 从结果集的开头开始扫描;
- 一直取到 offset + count 条;
- 扔掉前
offset条; - 返回后面的
count条给客户端。
👉 换句话说:
MySQL 必须先扫描 offset + count 行数据, 再丢弃 offset 行,只返回 count 行。
三、代入具体数字
你的 SQL:
LIMIT 100, 100
即:
- offset = 100
- count = 100
那么 MySQL 实际上会扫描 200 行:
扫描 200 行 → 丢掉前 100 行 → 返回 100 行。
四、更大的偏移量时的问题
假设语句:
SELECT * FROM orders LIMIT 1000000, 100;
MySQL 依然会:
- 扫描前 1,000,100 行;
- 丢掉前 1,000,000 行;
- 仅返回最后的 100 行。
⚠️ 性能问题:
- offset 越大,浪费越多;
- MySQL 没有“从第 N 条开始读取”的索引级跳转机制;
- 所以分页越深,查询越慢。
五、为什么 MySQL 要这么干?
MySQL 的执行计划是基于结果集顺序的:
- 没有 offset 索引直接跳过功能;
- 除非你手动提供一个有序字段(如自增 id 或时间戳);
- 否则它必须遍历前 offset 行,才能保证返回结果的排序正确。
六、如何优化深分页?
方法 1:基于主键或排序字段“范围分页”
例如原语句:
SELECT * FROM orders ORDER BY id LIMIT 1000000, 100;
可以改为:
SELECT * FROM orders WHERE id > 1000000 ORDER BY id LIMIT 100;
这样 MySQL 可以用索引定位到 id=1000001 的位置,然后顺序扫描 100 行,性能极快。
方法 2:记录上次分页的“游标位置”
前端分页时保存最后一条记录的 ID:
-- 第一次查询
SELECT * FROM orders ORDER BY id LIMIT 100;
-- 第二页查询
SELECT * FROM orders WHERE id > {last_id_of_prev_page} ORDER BY id LIMIT 100;
这叫 Keyset Pagination(基于键的分页),
可以避免深度偏移,尤其适合滚动加载、无限下拉列表等场景。
方法 3:子查询 + JOIN 减少数据传递量(部分场景)
SELECT o.* FROM orders o JOIN ( SELECT id FROM orders ORDER BY id LIMIT 1000000, 100 ) t ON o.id = t.id;
- 子查询只拿 ID(轻量);
- 再 JOIN 原表获取完整行;
- 比直接
LIMIT效率更好。
七、结论总结
| 语句 | 实际扫描行数 | 返回行数 | 特点 |
|---|---|---|---|
| LIMIT 100, 100 | 200 行 | 100 行 | 小偏移量,影响可忽略 |
| LIMIT 1000000, 100 | 1,000,100 行 | 100 行 | ⚠️ 深分页,极慢 |
| WHERE id > x LIMIT 100 | 100 行 | 100 行 | ✅ 推荐分页方式 |
八、总结
MySQL 的 LIMIT offset, count 会扫描 offset + count 行,返回 count 行。
当 offset 很大时,性能急剧下降,建议用基于主键的范围分页(Keyset Pagination) 代替。
到此这篇关于MySQL LIMIT 深分页性能问题与优化实战的文章就介绍到这了,更多相关MySQL LIMIT 深分页内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
