thinkphp深分页必须弃用paginate()改用id游标查询,因其底层强制count(*)和大offset导致mysql全索引扫描;应以id为锚点,首次查21条判断下一页,后续用where('id','

ThinkPHP 默认的 paginate() 在深分页(如第 1000 页、OFFSET 超过 10 万)下必然变慢,这不是配置或索引能救的——它底层强制执行 COUNT(*) 和大偏移 LIMIT offset, size,MySQL 必须扫描并丢弃大量行。唯一有效解法是绕开 paginate(),手写基于 ID 的游标查询。
为什么不能用 paginate() 做深分页
现象:第 1 页毫秒级,翻到第 500 页开始卡顿,第 2000 页直接超时;慢查询日志里反复出现 Using filesort 和 Using temporary。
-
paginate()内部先跑一条SELECT COUNT(*) FROM table WHERE ...,百万级表上这条就可能耗 1–2 秒 - 再跑
SELECT * FROM table WHERE ... ORDER BY id DESC LIMIT 10 OFFSET 19990,MySQL 得从索引头开始扫 19990 行再取 10 条,主键索引也压不住 - ThinkPHP 不支持把
WHERE id > ?注入进paginate()的 SQL 生成链,所有“改参数”“加 withTotal(false)”都只是省一次 COUNT,OFFSET 问题照旧
手写游标分页:三步写出稳定查询
核心就是不用 OFFSET,靠上一页最后一条的 id 当锚点,查“比它大的下一批”。前提是 id 是主键、非空、递增、有索引(默认都满足)。
- 首次请求(无游标):
Db::name('order')->order('id DESC')->limit(21)->select()—— 多查 1 条用于判断“是否有下一页” - 后续下一页:
Db::name('order')->where('id', 'order('id DESC')->limit(21)->select()—— 注意方向:DESC 对应 - 返回时只取前 20 条给前端,第 21 条的
id提出来作为next_cursor字段传回去 - 必须校验
$lastId是正整数:input('cursor', 0, 'intval'),否则 SQL 注入风险直连数据库
模型层封装 cursorPaginate() 避免重复出错
每次控制器里手写 where+order+limit 容易漏条件、错方向、忘校验。抽成模型方法最稳妥,还能统一加缓存和权限控制。
- 在
UserModel中加方法:public function cursorPaginate($cursor = null, $size = 20, $where = []) - 内部逻辑:若
$cursor存在,自动追加$this->where('id', '(方向按你 <code>order动态判断) - 返回结构建议固定为:
['data' => [...], 'has_next' => true, 'next_cursor' => 12345],不塞total或last_page - 调用时必须显式写
order:UserModel::order('id DESC')->cursorPaginate($cursor, 20),不写order就会失效
时间字段游标分页的坑:create_time 单独用大概率漏数据
用 create_time 分页看着合理,但高并发下同一秒插入多条记录,WHERE create_time > '2024-05-01 10:00:00' 会跳过其中部分——因为它们 create_time 完全一样,无法区分先后。
- 正确做法是双字段游标:
ORDER BY create_time DESC, id DESC,然后 WHERE 条件写成:WHERE (create_time, id) - ThinkPHP 6.1+ 支持数组形式的复合条件:
->where(['create_time', 'id'], ' - 如果用的是 6.0 或更早,就得手拼 SQL 或用
whereRaw,且必须确保两个字段都有联合索引 - 千万别用
ORDER BY RAND()或函数表达式(如ORDER BY ABS(id)),游标值无法稳定映射,分页直接错乱
游标分页快是真的快,但代价也很实在:它天然不支持跳转任意页码,也不能返回精确总数。如果你的业务需要“输入页码跳转”或“显示共 XXX 条”,那就得保留 paginate() 用于后台管理类场景,而用游标专供前端无限滚动、订单流、日志列表这类只“往下刷”的接口——混用可以,硬套不行。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











