论坛变慢,先别急着升级服务器或换 PHP 版本:按「慢查询日志定位 → EXPLAIN 看执行计划 → 补索引 → 应用层加缓存 → 排除外部请求」这五步走,绝大多数论坛站点的性能问题在前两步就能被定位。Clara BBS 没有编译缓存,模板和插件改完文件即生效,所以性能排查的重点几乎全部落在数据库查询和页面逻辑上,而不是「清缓存试试」。
论坛变慢从哪一步开始查?先开 MySQL 慢查询日志,不要先换配置
结论:没有慢查询日志的优化都是猜,第一步永远是让 MySQL 把超过 1 秒的 SQL 记下来。
在 MySQL 中临时开启(重启失效):
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;
SET GLOBAL log_queries_not_using_indexes = 0;
要永久生效,就写进 my.cnf 的 [mysqld] 段:
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
跑满 24 小时(覆盖早晚高峰)后用 mysqldumpslow -s t -t 20 /var/log/mysql/slow.log 排序,按「总耗时」而不是「单次耗时」排序,你会看到 3-5 条 SQL 吃掉 80% 的时间。论坛场景里高频出现的就那几类:版块列表按时间倒序、帖子详情页的关联查询、用户信息批量取、搜索用的 LIKE '%关键词%'。
EXPLAIN 里该看哪几个字段?
结论:只看三个字段——type、rows、Extra。type=ALL 且 rows 接近表总行数,就是全表扫描;Extra 里出现 Using filesort 或 Using temporary,说明排序和临时表没吃到索引。
EXPLAIN SELECT id,title,created_at FROM posts
WHERE forum_id = 12 AND status = 1
ORDER BY created_at DESC LIMIT 20;
理想结果是 type=ref、key 命中 (forum_id, status, created_at) 这样的联合索引、Extra 里不再有 Using filesort。注意联合索引的最左前缀原则:(forum_id, status, created_at) 能服务「版块 + 状态 + 排序」,而单独一个 created_at 索引在带 forum_id 条件时帮助有限。
论坛该给哪些字段加索引?
结论:优先补三类索引——列表页的「筛选列 + 排序列」联合索引、帖子与回复表的 user_id、以及回收站/审核状态类的低基数过滤列组合。
具体建议:
- 帖子表:
(forum_id, status, created_at)、(user_id, created_at)
- 回复表:
(post_id, created_at)、(user_id, created_at)
- 通知表:
(user_id, is_read, created_at)
- 附件/交易流水:
(user_id, created_at)
注意两点:一是 MySQL 5.7 不支持函数索引(8.0 才有),WHERE DATE(created_at) = '2025-01-01' 这类写法会直接放弃索引,改成 created_at >= '2025-01-01 00:00:00' AND created_at < '2025-01-02 00:00:00';二是索引不是越多越好,每加一个索引都会给写入(发帖、回帖)带来额外开销,论坛是读多写少但写入路径敏感的场景,索引表控制在合理的十几个以内。
大偏移分页也要处理:LIMIT 100000, 20 会先扫 10 万行再丢弃,改成游标式 WHERE id < 上次最大id ORDER BY id DESC LIMIT 20,耗时从秒级降到毫秒级。
应用层缓存怎么加?Cache::remember 用在哪一层
结论:缓存要加在「查库结果」这一层,而不是加在整页上;Clara BBS 插件开发中可直接用公共设施 Cache::remember 包住重复查询,键名带业务前缀、TTL 按数据变化频率给。
典型形态是把首页热门帖、版块统计、榜单这类「允许几分钟延迟」的数据缓存 300 秒:
$hot = Cache::remember('hot_posts_7d', 300, function () {
return $db->query("SELECT id,title FROM posts WHERE status=1 ORDER BY created_at DESC LIMIT 20");
});
几个实操注意点:缓存键必须带版本或业务前缀,避免不同插件撞键;数据本身变动频繁的(如帖子浏览量)不要进缓存,改用异步累加;定时任务用 Cron::register 注册懒触发任务,把统计类计算挪到低峰期跑。清理入口在后台「系统工具→缓存清理」。另外提醒:Clara BBS 的「改文件即生效」指的是没有模板编译产物需要清理,PHP 层若开了 OPcache,请保持 opcache.validate_timestamps=1(默认值),否则改完文件需要重启 PHP-FPM 才生效。
除了数据库,还有哪些慢是查不出来的?
结论:慢查询日志里看不到的耗时,通常来自外部 HTTP 请求和静态资源,尤其是第三方接口类插件。
排查顺序:① 插件是否在页面渲染时同步调用外部接口(AI 智能回复、API 工具箱的第三方解析、邮件发送),这类调用一旦对方超时 5 秒,页面就卡 5 秒,必须改为异步或加 2-3 秒超时;② 页面图片体积,单张手机原图常见 3-8 MB,建议上传侧限制尺寸并开启 PHP 的 upload_max_filesize 与 post_max_size 合理配置(宝塔默认 2M,建议调到 30M 以上再交由前端压缩);③ 钩子数量,Clara BBS 运行时提供 156 个钩子,每个启用插件都会在对应钩子点执行,启用十几个插件后要确认没有插件在列表页循环内查库(典型的 N+1)。
优化完怎么验证效果?
结论:用「慢查询条数 + 首屏 TTFB + 数据库 QPS」三个指标前后对比,别只看体感。
做法:优化前后各跑 24 小时慢查询日志,对比条数与总耗时;用浏览器开发者工具或 curl -o /dev/null -s -w '%{time_starttransfer}\n' https://你的域名/ 取样 20 次取中位数;数据库侧看 SHOW GLOBAL STATUS LIKE 'Questions' 的增速。三项都降下来,才算优化到位。
总结一下:论坛性能排查的顺序是「日志定位 → EXPLAIN 验证 → 补联合索引 → 结果层缓存 → 排除外部同步请求」,Clara BBS 因为无 Composer、无编译缓存、保存即生效,调优链路很短,真正的瓶颈基本都在 SQL 写法和插件里的同步外部调用上。先把慢查询日志开起来跑一天,你会发现要改的地方远比想象中少。