分页接口非幂等的根本原因是offset分页依赖动态快照,而数据库未锁定该快照;应改用带签名验证的游标分页,以排序字段+唯一键构建where条件,返回next_cursor并禁用page参数。

为什么分页接口会出现非幂等行为
分页接口看似只读,但实际常因后端数据动态变更(如新增/删除记录)导致同一 page=2&limit=10 请求在不同时刻返回不同结果。这不是 bug,而是「游标偏移」本质:基于偏移量(offset)的分页依赖全局有序快照,而数据库没有自动锁定该快照。
常见错误现象包括:
- 用户下拉刷新时重复加载同一批数据
- 点击“下一页”突然跳过几条记录(被新插入数据挤下去)
- 删除某条记录后,原第 3 页首条数据消失,用户误以为加载失败
根本原因在于用 OFFSET 做分页时,SELECT * FROM posts ORDER BY id DESC LIMIT 10 OFFSET 20 每次都重新计算偏移,不保证逻辑连续性。
用游标分页(cursor-based pagination)替代 offset 分页
游标分页不依赖数字页码,而是用上一页最后一条记录的排序字段值作为下一页起点,天然具备幂等性——只要游标值不变,查询结果就确定。
实操建议:
- 排序字段必须有唯一性约束或组合唯一(推荐
id或created_at,id) - 游标值应 URL-safe 编码(如 base64_encode()),避免直接暴露原始
id或时间戳 - 查询条件改写为:
WHERE created_at (多取 1 条用于生成下一页游标) - PHP 中解析游标:
$cursor = json_decode(base64_decode($_GET['cursor'] ?? ''), true);,确保字段顺序与 ORDER BY 一致
注意:不能用 WHERE id > $last_id 简单处理倒序分页,否则会漏掉相同时间戳的多条记录。
PHP 翻页函数里如何安全封装游标逻辑
一个健壮的翻页函数需隔离游标解析、SQL 构建和响应组装。不要把游标拼接进 SQL 字符串,更不要信任未校验的 $_GET['cursor']。
关键点:
- 游标必须带签名或有效期验证,防止篡改(例如:
base64_encode(json_encode([$id, $ts, $hash])),其中$hash = hash_hmac('sha256', "$id|$ts", $_ENV['CURSOR_SECRET'])) - 分页函数返回结构应包含
data、next_cursor(非空表示还有下一页)、has_more(布尔值,比判空next_cursor更可靠) - 不提供
page参数入口,彻底禁用 offset 模式;若旧接口需兼容,应在中间层做一次映射(查出第 N 页首条记录的游标再转发)
示例片段:
function paginatePosts($pdo, $cursor = null, $limit = 10) {
$sql = "SELECT id, title, created_at FROM posts WHERE 1=1";
$params = [];
<pre class="brush:php;toolbar:false;">if ($cursor) {
$decoded = json_decode(base64_decode($cursor), true);
// 验证签名、时效、字段数...
$sql .= " AND (created_at prepare($sql);
$stmt->execute($params);
$rows = $stmt->fetchAll();
$next_cursor = null;
if (count($rows) > $limit) {
$last = array_pop($rows);
$next_cursor = base64_encode(json_encode([$last['id'], $last['created_at']]));
}
return ['data' => $rows, 'next_cursor' => $next_cursor, 'has_more' => (bool)$next_cursor];}
哪些场景仍需谨慎对待游标分页
游标分页不是银弹。以下情况容易被忽略:
- 前端需要“跳转到指定页码”(如输入页码 17):此时无法用游标,只能回退到 offset 分页,但应加缓存或限制最大页码(如
page ) - 排序字段存在大量重复值且无二级键(如只按
status排序):游标无法精确定位,必须补全唯一索引字段 - 数据库主从延迟明显时,游标查询落在从库,可能读到未同步的新记录,导致下一页“多出”数据;解决方案是强制走主库,或接受最终一致性
最易踩的坑是:把游标当成透明 token 传递,不做任何解码校验,也不设有效期——攻击者可构造任意 id 绕过权限检查。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











