关掉count能大幅提升分页性能,因默认paginate执行count()和数据查询两条sql,百万级表中count()极慢;simple=>true仅查21条判断下一页存在性,响应达毫秒级。

ThinkPHP 大数据量分页必须关掉 count,否则一查就卡死;用 simple => true 模式可跳过总数统计,只查下一页是否存在,响应从秒级降到毫秒级。
为什么关掉总数就能快这么多
默认 paginate() 会执行两条 SQL:一条 SELECT COUNT(*) 算总条数,一条 SELECT ... LIMIT offset, size 取数据。在百万级表里,COUNT(*) 常耗时几百毫秒甚至几秒——尤其带复杂 WHERE 条件时,InnoDB 得扫索引或回表统计。
而多数业务根本不需要“共 1234567 条”这个数字,用户只关心“还有没有下一页”。关掉它,等于砍掉最重的一刀。
-
simple => true模式下,框架只执行一次查询:SELECT * FROM table WHERE ... ORDER BY id DESC LIMIT 21 - 取到 21 条,说明下一页有数据(返回前 20 条,第 21 条仅作存在判断)
- 取到 ≤20 条,说明已到底,前端可禁用“下一页”按钮
- 不查总数,不计算总页数,
render()只输出上/下页链接,无页码数字
怎么写 simple 分页代码
别用数字参数调用,必须显式传数组并开启 simple:
$list = Db::name('user')
->where('status', 1)
->order('id DESC')
->paginate([
'list_rows' => 20,
'simple' => true,
'query' => ['status' => 1] // 保持搜索参数
]);
注意几个关键点:
-
simple是布尔值,不是字符串,别写成'simple' => 'true' -
query参数必须显式传,否则翻页时 URL 会丢掉status=1这类条件 - 不能和
total参数混用——simple模式下total会被忽略 - 模板里用
$list->render()渲染,但不要调用$list->lastPage()或$list->total(),会报错或返回 null
simple 模式下如何判断是否到底
框架不提供“是否最后一页”的直接方法,但逻辑很轻量:
- 你拿到的
$list数据是截断后的(最多 20 条),但内部已多查了 1 条用于判断 - 检查
$list->hasMore()—— ThinkPHP 6.0+ 原生支持,返回布尔值 - ThinkPHP 5.1 需手动判断:
count($list) 表示没凑满,即已到底 - 前端按钮状态可基于此控制:
if (!$list->hasMore()) { echo 'disabled'; }
容易被忽略的兼容性细节
simple 模式看着简单,但有几个硬约束常被踩坑:
- 不支持
appends()方法补参,必须用query数组一次性注入所有需保留的 GET 参数 - 若用
order('created_at DESC'),确保created_at字段有索引,否则LIMIT 21仍可能慢 - 不能和
->page(1)这类链式调用混用,page参数必须塞进 paginate 数组里,否则无效 - API 场景下,
render()生成的是 HTML 字符串,不适合 JSON 接口——此时应直接取$list->items()和$list->hasMore()手动构造响应
真正要命的不是性能本身,而是你以为关了 count 就万事大吉,结果发现翻页链接丢了搜索条件、或者 API 返回了 HTML 片段——这些地方不显眼,但上线后立刻暴露。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











