MySQL分页用LIMIT和OFFSET时,OFFSET越大性能越差,因其需扫描并丢弃前N行;应确保ORDER BY字段有索引、校验offset参数、避免深分页,推荐游标分页替代。
LIMIT 和 OFFSET 是 MySQL 分页最直接的实现方式,但直接套用容易出错——尤其是 OFFSET 值变大时性能会断崖式下降。
为什么 OFFSET 越大越慢?
MySQL 在执行
时,并不是跳过前 10000 行再取数据,而是先扫描并丢弃前 10000 行结果,再返回后续行。这意味着分页越靠后,扫描量越大,I/O 和 CPU 开销越高。
表有 100 万行,
实际要读取至少 50 万 +
行
若未命中索引,还可能触发全表扫描
高并发下容易堆积慢查询,拖垮整个数据库
如何写一个安全可用的分页 SQL?
基础结构必须带排序和主键/唯一索引约束,否则分页结果可能重复或遗漏:
关键点:
立即学习
“
PHP免费学习笔记(深入)
”;
PHP 8.5.5
PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。
下载
字段必须有索引(如
是主键,天然有索引)
避免用
这类可能重复的字段做分页锚点,除非加
作为第二排序条件
值需校验:不能为负数,不能超过总行数(建议前端传参时就限制最大页码)
PHP 中拼接前务必用
或
过滤
PHP 中如何计算和传递分页参数?
不要依赖“当前页 × 每页条数”硬算
,而应从请求中明确接收
和
(或
),并做防御性处理:
用
获取每页数量,上限建议设为 100,防止恶意拉取大量数据
必须
处理
查询总数用
单独执行(注意:不要用
,已废弃且不准)
若总数超 10 万,可考虑不显示总页数,改用“下一页是否存在”逻辑(查
,只取前 $limit 行,多出来那 1 行判断是否有下一页)
什么时候该放弃 OFFSET?
当用户明显进入深分页(比如
),或者业务允许“游标分页”时,应切换方案:
用上一页最后一条记录的
作为下一页起点:
游标分页无法跳转任意页,但性能稳定、无偏移累积误差
对时间线类数据(如动态、日志),用
+
组合游标更自然
PHP 中需把游标值(如
)作为
返回给前端,而不是
OFFSET 看似简单,但真实系统里最容易在高偏移、无索引、未过滤参数这几个点上翻车。真正要稳,得从 SQL 结构、PHP 参数校验、前端交互逻辑三处同时卡住。
OFFSET 10000OFFSET 500000LIMITSELECT * FROM users WHERE status = 1 ORDER BY id ASC LIMIT 20 OFFSET 40;ORDER BYidORDER BY created_atidOFFSETintval()filter_var($offset, FILTER_VALIDATE_INT)OFFSETlimitoffsetpage$_GET['limit'] ?? 20$_GET['offset'] ?? 0max(0, intval($_GET['offset']))SELECT COUNT(*)SQL_CALC_FOUND_ROWSLIMIT $limit + 1OFFSET > 10000idWHERE id > 12345 ORDER BY id ASC LIMIT 20created_atid12345next_cursorpage=501