高并发帖子列表分页加载优化

CLARA轻量论坛系统
CLARA轻量论坛系统 星耀SVIP管理员 黑卡会员
发布于 2026-09-19 17:49 ·2 浏览 ·0 回复

高并发下帖子列表分页的性能瓶颈,99% 不在「分页」本身,而在每次请求都做了一次全表 `COUNT(*)` 加一次深翻 `OFFSET`;把这两件事拆掉——热门页走缓存、深翻页改游标、列表查询走覆盖索引——单机撑住几千 QPS 的列表页并不难。

先干掉 COUNT(*),它比你想的贵得多

结论:帖子列表页最大的隐形开销是分页总数统计,而不是取那 20 条数据。

`SELECT COUNT(*) FROM posts WHERE fid=3 AND status=1` 在百万级表上就是一次全索引扫描,而它每次翻页都要跑一遍。三种处理方式,按性价比排序:

  1. 估算 + 封顶:页码只显示到前 N 页(比如 100 页),超过就提示「请用搜索或时间筛选」,避免用户翻到第 8000 页。这是社区场景最实用的做法——没人真的翻到第 8000 页。
  2. 缓存计数:把每个版块的帖子数写进缓存,发帖/删帖时增减,而不是每次 `COUNT`。
  3. 游标分页(keyset pagination):用 `WHERE created_at < ? ORDER BY created_at DESC LIMIT 20` 替代 `LIMIT 20 OFFSET 40000`。深翻时 `OFFSET` 会先扫过并丢弃前 4 万行,游标分页直接走索引定位,第 1 页和第 1000 页耗时几乎一样。

注意点:游标分页拿不到「第几页」,只能给「下一页」,所以通常和上面的「页码封顶」配合用——前台保留页码,深翻入口收敛。

热的永远是第一页,把它缓存起来

结论:论坛列表流量高度集中在第 1-3 页,缓存这几页的收益远大于优化深翻页。

Clara BBS 的插件公共设施里有 `Cache::remember`,思路就是它:

Cache::remember('forum_list_fid3_page1', 60, function () {
    // 查 20 条帖子 + 作者信息 + 分类名
});

三个必须注意的点:

  • 缓存键要带维度:版块 ID、页码、分类筛选、用户组(权限不同看到的帖子不同)都要进键名,否则会把隐藏版块的帖子缓存给游客。
  • TTL 别太长:列表页 30-120 秒足够,发帖后靠主动清缓存或短 TTL 自然过期。Clara BBS 本身是「保存即生效、无需清缓存」的设计,缓存属于你额外加的层,失效逻辑要自己写清楚。
  • 别缓存整页 HTML 给登录用户:登录态、头像、未读通知都在页面里,缓存的应该是数据层,不是渲染结果。

想做首页/版块页的「伪静态化」,可以配 `Cron::register` 注册一个懒触发的定时任务,每隔几分钟把那几页渲染成静态文件,请求直接吐文件。

索引和组织结构,决定查询是 5ms 还是 500ms

结论:帖子列表的黄金索引是「过滤字段在前、排序字段在后」,让它能同时完成 WHERE 和 ORDER BY。

典型列表查询是「某版块 + 正常状态 + 按时间倒序」,对应索引大致是:

ALTER TABLE posts ADD INDEX idx_fid_status_time (fid, status, created_at);
-- 需要置顶排前时:(fid, status, is_top, created_at)

几个容易踩的坑:

  • 软删除要进条件:Clara BBS 前台删除是软删除(进回收站保留 30 天,天数可配),列表查询必须带上状态条件,否则回收站里的帖子会漏出来,而且 `COUNT` 也会算多。
  • **慎用 `SELECT *`**:列表只需要标题、作者、时间、回复数。字段越少,越可能走覆盖索引、免回表。要正文摘要就单独查,或用延迟关联(先用索引查出 20 个 ID,再用主键查详情)。
  • 多表 JOIN 要控制:作者名、版块名、分类名如果每次 JOIN,压力会翻倍。作者信息可以整表缓存在内存里按 ID 取。

页面层也有优化空间,而且改起来最省事

结论:减少每页条数、限制最大页码、引导用户少翻页,这三件在后台和产品层就能做,成本最低。

  • 后台「显示分页」相关设置里把每页条数从 20 调到 15-20 之间即可,别为了「看起来内容丰富」设成 50。
  • 用好全文阅读模式(带目录与进度条)和长文折叠:用户在一个页面里读完,比翻五页更省资源。
  • URL 走 `.html` 伪静态,服务器只需把非静态请求转发给 `index.php`(Nginx `try_files` 或 Apache `.htaccess`),无需额外规则;配合 CDN 时静态化收益更明显。
  • 版块内的子分类筛选、置顶/加精/推荐等筛选条件,都会改变查询组合,索引设计要把常用组合覆盖到,冷门组合宁可慢一点。

最后提醒两件事

一是权限优先于性能:任何缓存和静态化输出,都必须只包含游客可见内容,隐藏版块、权限版块绝不能进公共缓存或 GEO 输出——Clara BBS 的 GEO 输出本身就是这个口径,你加缓存时要守住同样的边界。

二是先量后调:上线前用慢查询日志找出真正慢的那条 SQL,再决定是加索引、加缓存还是改游标分页。绝大多数站点根本到不了「高并发」那一步,先扛住第 1 页的缓存,就能解决八成的列表页压力。

本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-520.html
转载请注明出处,版权归原作者所有。

全部回复 0

还没有回复,来抢沙发~