PHP分页核心是正确计算offset=($page-1)*$limit和固定limit值,需校验$page为≥1整数、$total有效性及避免大offset性能问题。

PHP分页的核心是计算 offset 和 limit
分页不是“显示第几页”,而是“从哪条数据开始取、取多少条”。offset 是起始位置(从 0 开始),limit 是每页数量。数据库查的时候直接用这两个值,前端只负责传当前页码 $page 进来。
常见错误:把 $page = 1 直接当 offset 用,结果第一页就漏掉首条数据;或者没校验 $page 是否为正整数,导致 SQL 报错或越界查询。
-
$page必须强制转为整型,并且最小值设为 1:$page = max(1, (int)$_GET['page'] ?? 1) -
$limit建议固定(比如 10 或 20),不要让用户传,避免恶意调大导致慢查询 -
$offset = ($page - 1) * $limit—— 这个公式不能错,减 1 是关键 - 查总数时别用
COUNT(*)套在带LIMIT的语句里,要单独执行一次无 limit 的COUNT(*)
如何安全获取总页数并避免除零错误
总页数 = ceil($total / $limit),但前提是 $total 是整数且 $limit > 0。实际中常遇到 $total 查询失败返回 false 或 null,直接参与运算会触发警告甚至页面崩掉。
- 先检查
$total是否为有效数字:is_numeric($total) && $total >= 0 -
$limit必须大于 0,否则ceil()会出错,建议硬编码或白名单校验 - 如果
$total === 0,总页数就是 1(空列表也该有第一页),别让它变成 0 - 示例:
$pages = $total > 0 ? ceil($total / $limit) : 1;
生成分页 HTML 链接时 URL 参数容易混乱
用户当前可能已在带参数的列表页(比如 ?category=php&sort=date),直接拼 ?page=2 会丢掉原有参数。用 http_build_query() 重写 URL 最稳妥。
立即学习“PHP免费学习笔记(深入)”;
- 先取出原始
$_GET,再覆盖page键:$params = $_GET; $params['page'] = $i; - 用
http_build_query($params)拼出完整 query string,避免手动拼接漏转义 - 注意过滤掉可能干扰分页的键,比如
page本身要保留,但search这类大文本字段建议不参与分页链接(否则 URL 超长) - 别用
$_SERVER['REQUEST_URI']简单替换,它包含路径和已有 query,手动处理极易出错
MySQL LIMIT 大偏移量性能差怎么办
当 $page 很大(比如第 10000 页),OFFSET 99990 会让 MySQL 扫描前 99990 行再丢弃,响应明显变慢。这不是 PHP 能解决的,但 PHP 层可以规避。
- 限制最大可访问页码,比如
if ($page > 500) { die('超出范围'); } - 改用游标分页(cursor-based pagination):用上一页最后一条记录的
id或时间戳作为下一页起点,例如WHERE id > 12345 ORDER BY id LIMIT 10 - 对搜索类接口,考虑加缓存层(如 Redis 缓存分页结果),但需注意数据实时性要求
- 如果必须支持深度分页,确保
ORDER BY字段有索引,且尽量避免SELECT *,只查必要字段
真正难的不是写出分页代码,而是想清楚「谁在翻到第 500 页?他到底需要什么?」——很多时候,用户要的不是跳转,而是筛选或搜索。分页逻辑越健壮,越要警惕它掩盖了更本质的产品问题。



















