MyBB 1.8.41 常用运维命令与排障详解

MyBB 1.8.41 日常运维命令与排障实操:mysqldump 备份与灾难恢复演练、crontab 定时备份、管理员密码重置(双层加盐 md5 算法实测+前台/后台登录验证)、datacache 缓存与计数统计重建、插件冲突排障(坏插件定位、插件表丢失全站 500 事故链、SQL 紧急停用与残留清理)、错误日志定位,全部命令在 Ubuntu 22.04 实测。

MyBBMyBBMariaDB运维2026/10/30

MyBB 1.8.41 常用运维命令与排障详解

目标:覆盖 MyBB 1.8.41 上线后的日常运维命令与高频事故排障——数据库备份与灾难恢复演练、定时备份、管理员密码重置(按 MyBB 密码算法实测并登录验证)、数据缓存/计数/统计重建、插件激活与冲突排障(含"插件表丢失导致全站 500"完整事故链实录)、错误日志定位。全部命令与回显均在 Ubuntu 22.04(Apache 2.4.52 + PHP 8.1.2 + MariaDB 10.6.23)实测环境逐条跑通。

一、环境信息

  • 操作系统:Ubuntu 22.04 LTS(x86_64)
  • Web 服务器:Apache 2.4.52(Ubuntu 官方源),站点配置 /etc/apache2/sites-available/mybb.conf,DocumentRoot /var/www/mybb
  • PHP:8.1.2-1ubuntu2.28(apache2 handler 模块方式运行)
  • 数据库:MariaDB 10.6.23(mysql Ver 15.1 Distrib 10.6.23-MariaDB),数据库名 mybbdb,业务账号 mybb,表前缀 mybb_
  • MyBB:1.8.41(安装于 /var/www/mybb,后台目录 admin/,前台 http://localhost/index.php,首页标题实测 MyBB Demo Forum)
  • 备份目录:/var/backups/(本文全部备份产物落在此处)
  • 演示管理账号:admin / q1w2e3r4(前台与 Admin CP 同账号,用户组为管理员组 uid=1)

💡 技术拓展:MyBB 的密码不是以明文或简单 md5 存储的。同类论坛软件的口令存储方案对比——

方案原理代表软件说明
双层加盐 md5md5(md5(salt) + md5(密码)),每用户独立随机 saltMyBB(本文所用)1.8 系列沿用;salt 存 mybb_users.salt
bcrypt自适应代价哈希,盐内嵌于哈希串phpBB 3.3、Flarum无需单独 salt 列
Argon2id内存困难型哈希Discourse(外部认证侧)抗 GPU 暴破更强
phpBB 旧式 md5md5(密码) 单层phpBB 3.0(已淘汰)已被 bcrypt 替代

MyBB 1.8.41 实际采用双层加盐 md5,本文第五章的重置操作完全围绕该算法展开,第五章 5.1 用 SQL 实测核对了这一等式。

二、工具概览

MyBB 本体不带 CLI 命令行工具(区别于 NodeBB 的 nodebb CLI 或 Flarum 的 flarum 命令),日常运维入口分四类:

运维入口载体典型用途本文章节
MySQL 命令行mysql / mysqldump / mysqlcheck备份恢复、密码重置、插件紧急停用、缓存直查四、五、六、八、十
Admin CP 维护工具后台 Tools & Maintenance 菜单Cache Manager 缓存重建、Recount & Rebuild 计数统计重建、任务计划六、七
MyBB 内置日志表mybb_adminlog 等 6 张日志表后台操作审计、任务执行历史九
系统日志/var/log/apache2/ + PHP 错误配置Fatal/Parse error 定位、请求审计九

其中 MySQL 命令行承担了绝大多数"后台打不开时"的救急操作,是本文重点。

三、前置条件

  • 已按 《Ubuntu 22.04 安装 MyBB 指南》 完成安装并能打开论坛前台与 Admin CP;
  • 拥有 mybbdb 库的业务账号 mybb(安装向导创建,具备本库全部权限,可直接执行 mysqldump 与数据修复 SQL);
  • 已登录 Admin CP(http://localhost/admin/,账号 admin),用于后台工具章节的实测;
  • 可通过 SSH 登录服务器并具备 sudo/root 权限(日志、crontab、文件操作需要);
  • 涉及数据库的每一章动手前,建议先按第四章完成一次全量备份。

四、MySQL 数据库备份与恢复

4.1 全量备份(mysqldump)

MyBB 的全部业务数据都在 mybbdb 一个库的 75 张表里(含 6 张日志表),单库 mysqldump 逻辑备份即可覆盖全量。先确认当前数据基线:

# 统计库内表数
mysql -u mybb -pq1w2e3r4 -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='mybbdb';"

实测回显:

75
# 关键数据量基线(users/threads/posts/caches 四项)
mysql -u mybb -pq1w2e3r4 mybbdb -e "SELECT (SELECT COUNT(*) FROM mybb_users) users,(SELECT COUNT(*) FROM mybb_threads) threads,(SELECT COUNT(*) FROM mybb_posts) posts,(SELECT COUNT(*) FROM mybb_datacache) caches;"

实测回显:

users	threads	posts	caches
1	1	2	31

执行全量备份并核对备份文件结构:

# 全量备份(--databases 带建库语句,恢复时免手工建库语义更完整)
mysqldump -u mybb -pq1w2e3r4 --databases mybbdb > /var/backups/mybbdb_full.sql
# 备份文件大小
ls -lh /var/backups/mybbdb_full.sql

实测回显:

-rw-r--r-- 1 root root 875K Oct 30 14:19 /var/backups/mybbdb_full.sql
# 备份文件内 CREATE TABLE 语句总数(应等于表数 75)
grep -c "CREATE TABLE" /var/backups/mybbdb_full.sql
# 抽查前 5 条建表语句位置
grep -n "CREATE TABLE" /var/backups/mybbdb_full.sql | head -5

实测回显:

75
34:CREATE TABLE `mybb_adminlog` (
63:CREATE TABLE `mybb_adminoptions` (
96:CREATE TABLE `mybb_adminsessions` (
126:CREATE TABLE `mybb_adminviews` (
160:CREATE TABLE `mybb_announcements` (

备份文件 875K、75 条 CREATE TABLE 与库内表数一致,说明全量覆盖无遗漏。

💡 技术拓展:数据库备份方案不止 mysqldump 一种——

方案类型特点适用
mysqldump / mariadb-dump逻辑备份生成可读 SQL,跨版本兼容好,单机中小库首选本文采用(75 表 875K 量级完全够用)
XtraBackup物理热备不锁表、TB 级库适用大流量高并发论坛
LVM/文件系统快照物理快照秒级完成,依赖卷管理可接受短暂暂停的维护窗口
ACP Database BackupsMyBB 内置后台一键导出 gzip SQL(底层同为 SQL 导出)无 SSH 权限时的替代入口

本文按单机中小站规模采用 mysqldump 逻辑备份;ACP 后台的 Database Backups 入口(Tools & Maintenance → Database Backups)导出的也是同构 SQL,可作为无命令行权限时的替代。

4.2 压缩与定时备份

原始 dump 压缩比约 6.4:1:

# gzip 压缩(-c 保留原文件)
gzip -c /var/backups/mybbdb_full.sql > /var/backups/mybbdb_full.sql.gz
ls -lh /var/backups/mybbdb_full.sql.gz

实测回显:

-rw-r--r-- 1 root root 136K Oct 30 14:19 /var/backups/mybbdb_full.sql.gz

配合 crontab 做每日定时备份(Ubuntu Server 自带 cron;本实测环境需先 apt-get install -y cron):

# 追加每日 03:30 定时备份任务(保留日期后缀,按天滚动)
(crontab -l 2>/dev/null; echo "30 3 * * * mysqldump -u mybb -pq1w2e3r4 --databases mybbdb | gzip > /var/backups/mybbdb_\$(date +\%Y\%m\%d).sql.gz") | crontab -
# 查看已生效的定时任务
crontab -l

实测回显:

30 3 * * * mysqldump -u mybb -pq1w2e3r4 --databases mybbdb | gzip > /var/backups/mybbdb_$(date +\%Y\%m\%d).sql.gz

验证定时任务里的备份管道可独立执行(与 cron 调用的命令一致):

# 手动按同一管道执行一次
mysqldump -u mybb -pq1w2e3r4 --databases mybbdb | gzip > /var/backups/mybbdb_daily.sql.gz
ls -lh /var/backups/mybbdb_daily.sql.gz

实测回显:

-rw-r--r-- 1 root root 136K Oct 30 14:26 /var/backups/mybbdb_daily.sql.gz

⚠️ 注意:crontab 里 % 是特殊字符(表示换行),必须写成 \% 转义,否则 date 命令在 cron 环境下会被截断导致备份文件名异常;实测条目中的 \$(date +\%Y\%m\%d) 已含转义。验证无误后用 crontab -r 清除了本次测试条目(生产按需保留)。

4.3 灾难恢复演练(DROP → 重建 → 恢复 → 验证)

本节完整演练"误删库后从备份恢复"的全过程,命令与回显均为实测照抄。动手前确认 4.1 的备份文件存在。

第一步,记录恢复前基线(75 表、四项计数见 4.1),然后模拟灾难:

# 模拟误删整库
mysql -uroot -e "DROP DATABASE mybbdb;"
# 确认库已不存在(计数应为 0)
mysql -uroot -N -e "SHOW DATABASES;" | grep -c mybbdb

实测回显:

0

第二步,确认业务账号是否幸存——MariaDB 的用户账号与权限独立于业务库存在,DROP DATABASE 不会删除 mybb 账号:

mysql -uroot -e "SELECT user,host FROM mysql.user WHERE user='mybb';"

实测回显:

User	Host
mybb	localhost

第三步,重建库并导入备份(无需重建账号授权):

# 重建库(字符集与排序规则须与原库一致,MyBB 官方推荐 utf8mb4_unicode_ci)
mysql -uroot -e "CREATE DATABASE mybbdb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
# 用业务账号直接导入全量备份
mysql -u mybb -pq1w2e3r4 mybbdb < /var/backups/mybbdb_full.sql
echo $?

实测回显:

0

第四步,恢复后全面验证:

# 表数核对
mysql -u mybb -pq1w2e3r4 -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='mybbdb';"
# 关键数据量核对(应与 4.1 基线完全一致)
mysql -u mybb -pq1w2e3r4 mybbdb -e "SELECT (SELECT COUNT(*) FROM mybb_users) users,(SELECT COUNT(*) FROM mybb_threads) threads,(SELECT COUNT(*) FROM mybb_posts) posts,(SELECT COUNT(*) FROM mybb_datacache) caches;"
# 前台与标题验证
curl -s -o /dev/null -w "forum:%{http_code}\n" http://localhost/index.php
curl -s http://localhost/index.php | grep -oE "<title>[^<]+" | head -1

实测回显:

75
users	threads	posts	caches
1	1	2	31
forum:200
<title>MyBB Demo Forum

75 表、四项计数、前台 200 与标题全部与事故前一致,恢复闭环完成。

4.4 表检查与优化(mysqlcheck)

例行维护用 mysqlcheck 做全库分析(--analyze 更新索引统计)与优化(--optimize 回收空间),InnoDB 表通常返回 OK 或 already up to date:

# 全库分析(输出节选,实测共 75 行)
mysqlcheck -u mybb -pq1w2e3r4 --analyze mybbdb 2>/dev/null | head -8

实测回显:

mybbdb.mybb_adminlog                               OK
mybbdb.mybb_adminoptions                           OK
mybbdb.mybb_adminsessions                          OK
mybbdb.mybb_adminviews                             OK
mybbdb.mybb_announcements                          Table is already up to date
mybbdb.mybb_attachments                            Table is already up to date
mybbdb.mybb_attachtypes                            OK
mybbdb.mybb_awaitingactivation                     Table is already up to date
# 全库优化(输出节选:InnoDB 表多数直接报 already up to date)
mysqlcheck -u mybb -pq1w2e3r4 --optimize mybbdb 2>/dev/null | grep -v "OK$" | head -3

实测回显:

mybbdb.mybb_adminsessions                          Table is already up to date
mybbdb.mybb_awaitingactivation                     Table is already up to date
mybbdb.mybb_badwords                               Table is already up to date

75 表全部分析通过、无损坏告警。ACP 后台的对应入口是 Tools & Maintenance → Optimize Database,效果等同。

五、管理员密码重置(排障实录核心)

忘记 admin 密码(或接手旧站)是论坛运维最高频事故。MyBB 不能在数据库里"直接写一个新 md5 密码"——它采用双层加盐算法,直接 SET password=MD5('新密码') 必然登录失败。本节按事故现场流程实测:先核对算法 → 模拟事故 → 两种重置方法 → 登录验证。

5.1 密码算法核对(实测确认)

先看当前库里的密码字段结构:

mysql -u mybb -pq1w2e3r4 mybbdb -e "SELECT uid,username,salt,password FROM mybb_users WHERE uid=1;"

实测回显:

uid	username	salt	password
1	admin	QBoTBMzd	c695b88e2c2be19ec498966ad6290276

mybb_users 表两个字段承担口令存储:salt(8 位随机盐,每用户独立)与 password(32 位 md5)。用 SQL 一步核对"MyBB 密码 = md5(md5(salt) + md5(密码))"这一等式(以演示密码 q1w2e3r4 为例):

mysql -u mybb -pq1w2e3r4 mybbdb -e "SELECT MD5(CONCAT(MD5(salt), MD5('q1w2e3r4'))) AS computed, password AS stored, MD5(CONCAT(MD5(salt), MD5('q1w2e3r4')))=password AS match_ FROM mybb_users WHERE uid=1;"

实测回显:

computed	stored	match_
c695b88e2c2be19ec498966ad6290276	c695b88e2c2be19ec498966ad6290276	1

computed 与 stored 完全一致、match_=1,算法实测确认。重置密码的 UPDATE 必须按这个结构生成哈希, salt 与哈希必须成对更新。

5.2 方法一:保盐改哈希(快速)

模拟事故——admin 密码被改成 Test123456(salt 保持不变,只按算法重算哈希):

mysql -u mybb -pq1w2e3r4 mybbdb -e "UPDATE mybb_users SET password=MD5(CONCAT(MD5(salt),MD5('Test123456'))) WHERE uid=1; SELECT salt,password FROM mybb_users WHERE uid=1;"

实测回显:

salt	password
QBoTBMzd	087ac75df76687ce65a8bea7ed285128

验证:旧密码 q1w2e3r4 登录应失败。用 curl 模拟完整登录(先从登录页取 CSRF 令牌 my_post_key,再 POST 到 member.php):

cd /tmp && rm -f f1.jar
# 第一步:带 Cookie 访问登录页,抽取 my_post_key
KEY=$(curl -s -c f1.jar "http://localhost/member.php?action=login" | grep -oE "my_post_key\" type=\"hidden\" value=\"[a-z0-9]{32}\"" | grep -oE "[a-z0-9]{32}" | head -1)
# 第二步:用旧密码提交登录
curl -s -b f1.jar -c f1.jar -d "action=do_login&url=&quick_login=0&quick_username=admin&quick_password=q1w2e3r4&my_post_key=$KEY&submit=Login" "http://localhost/member.php" -o fail.html -w "old_login:%{http_code}\n"

实测回显:

old_login:200

失败响应页中的真实报错文案(fail.html 原文照抄):

You have entered an invalid username/password combination. If you have forgotten your password please retrieve a new one.

再用新密码 Test123456 登录并访问首页确认登录态(登录成功后首页导航会出现退出链接):

rm -f f2.jar
KEY=$(curl -s -c f2.jar "http://localhost/member.php?action=login" | grep -oE "my_post_key\" type=\"hidden\" value=\"[a-z0-9]{32}\"" | grep -oE "[a-z0-9]{32}" | head -1)
curl -s -b f2.jar -c f2.jar -d "action=do_login&url=&quick_login=0&quick_username=admin&quick_password=Test123456&my_post_key=$KEY&submit=Login" "http://localhost/member.php" -o ok.html -w "new_login:%{http_code}\n"
# 登录态验证:首页应出现 logout 链接
curl -s -b f2.jar http://localhost/index.php | grep -oE "member\.php\?action=logout" | head -1

实测回显:

new_login:200
member.php?action=logout

旧密码失败、新密码成功,方法一闭环验证通过。

5.3 方法二:新盐一步重置(推荐,与 MyBB 自身改密行为一致)

MyBB 用户在后台修改密码时会同时再生 salt。数据库重置也推荐一步完成"生成新盐 + 按新盐算哈希",一条 UPDATE 内 salt 先赋值、password 引用更新后的 salt(MySQL 同一 UPDATE 内右侧列取新值):

mysql -u mybb -pq1w2e3r4 mybbdb -e "UPDATE mybb_users SET salt=SUBSTRING(MD5(RAND()),1,8), password=MD5(CONCAT(MD5(salt),MD5('q1w2e3r4'))) WHERE uid=1; SELECT salt,password FROM mybb_users WHERE uid=1;"

实测回显:

salt	password
b33448de	88ae209d91e0e63ac97547f1bfc14f6f

salt 已从 QBoTBMzd 再生为 b33448de,哈希随之变化。重置后用 q1w2e3r4 登录验证:

rm -f f3.jar
KEY=$(curl -s -c f3.jar "http://localhost/member.php?action=login" | grep -oE "my_post_key\" type=\"hidden\" value=\"[a-z0-9]{32}\"" | grep -oE "[a-z0-9]{32}" | head -1)
curl -s -b f3.jar -c f3.jar -d "action=do_login&url=&quick_login=0&quick_username=admin&quick_password=q1w2e3r4&my_post_key=$KEY&submit=Login" "http://localhost/member.php" -o ok2.html -w "reset_login:%{http_code}\n"
curl -s -b f3.jar http://localhost/index.php | grep -oE "member\.php\?action=logout" | head -1

实测回显:

reset_login:200
member.php?action=logout

5.4 Admin CP 后台登录验证

密码重置最终要看后台能否进入。Admin CP 登录无需 CSRF 令牌,直接 POST:

rm -f acp2.jar
curl -s -c acp2.jar -X POST "http://localhost/admin/index.php" --data "do=login&username=admin&password=q1w2e3r4" -o /dev/null -w "acp_login:%{http_code}\n"
# 登录后访问控制台页面验证
curl -s -b acp2.jar "http://localhost/admin/index.php?module=dashboard" | grep -oE "<title>[^<]+" | head -1

实测回显:

acp_login:200
<title>Dashboard

前台与后台双向验证通过,密码重置完整闭环。

⚠️ 注意:多次输错密码会触发登录尝试锁定(设置项 loginattempts 控制次数,超限后进入 awaitingactivation/锁定流程)。用数据库重置密码前,若站点开启了登录锁定,同步清零可避免"密码对了却仍提示错误": UPDATE mybb_users SET loginattempts=0, loginlocked=0 WHERE uid=1;(字段是否存在以 DESC mybb_users 实查为准)。

六、数据缓存重建

MyBB 把版块树、权限矩阵、用户组、表情、插件清单等热点数据序列化缓存在 mybb_datacache 表(默认缓存后端为 db,各后端对比见 《MyBB 1.8.41 配置参数详解》)。缓存行损坏或手工改库后不同步时,需要重建。本节实测两种重建路径:SQL 直删后前台按需自动重建、ACP Cache Manager 手工重建。

6.1 缓存清单查看

# 列出全部缓存项与序列化体积(实测 31 项)
mysql -u mybb -pq1w2e3r4 mybbdb -e "SELECT title, LENGTH(cache) FROM mybb_datacache ORDER BY title;"

实测回显(31 行全量照抄):

attachtypes	7037
awaitingactivation	46
badwords	6
bannedemails	6
bannedips	6
birthdays	6
default_theme	3530
forumpermissions	14
forums	2346
forumsdisplay	6
groupleaders	6
internal_settings	68
moderators	34
mostonline	49
mycode	6
plugins	25
posticons	1931
profilefields	1677
reportedcontent	69
reportreasons	1280
smilies	4173
spiders	1712
statistics	102
stats	266
tasks	38
threadprefixes	6
update_check	34
usergroups	19467
usertitles	620
version	60
version_history	843

其中 forums(版块树)、forumpermissions(权限矩阵)、usergroups(用户组)是与前台展示最相关的三张大缓存。

6.2 实测一:SQL 删除后前台按需自动重建

MyBB 前台读取缓存行不存在时会自动从源表重建并回写 mybb_datacache。实测验证这一机制:

# 删除 forums 缓存行(模拟缓存损坏)
mysql -u mybb -pq1w2e3r4 mybbdb -e "SELECT COUNT(*) AS before_del FROM mybb_datacache WHERE title='forums';"
mysql -u mybb -pq1w2e3r4 mybbdb -e "DELETE FROM mybb_datacache WHERE title='forums';"
mysql -u mybb -pq1w2e3r4 mybbdb -N -e "SELECT COUNT(*) FROM mybb_datacache WHERE title='forums';"

实测回显:

before_del
1
0
# 访问一次前台,触发按需重建
curl -s -o /dev/null -w "forum:%{http_code}\n" http://localhost/index.php
# 复查缓存行
mysql -u mybb -pq1w2e3r4 mybbdb -e "SELECT title,LENGTH(cache) AS rebuilt_size FROM mybb_datacache WHERE title='forums';"

实测回显:

forum:200
title	rebuilt_size
forums	2346

删除后计数 0,前台一次请求即自动重建出 2346 字节(与 6.1 清单中原始体积一致),前台无任何报错。

6.3 实测二:ACP Cache Manager 手工重建

后台路径:Tools & Maintenance → Cache Manager(admin/index.php?module=tools-cache)。页面按行列出各缓存项与体积,每行有 Rebuild Cache 链接,页头另有 Reload All。后台所有操作链接都带会话级 CSRF 令牌 my_post_key(同一登录会话内恒定),从任一后台页面源码抽取即可:curl -s -b acp2.jar "http://localhost/admin/index.php?module=tools-cache" | grep -oE "my_post_key=[a-z0-9]+" | head -1(实测本会话取值 2f366dee58e7d05d421b91c12a3f5593,下文各后台请求同用此值)。实测先用 SQL 删除 forums 行,再用后台按钮重建:

mysql -u mybb -pq1w2e3r4 mybbdb -e "DELETE FROM mybb_datacache WHERE title='forums';"
# (后台页面点击 forums 行的 Rebuild Cache 按钮,等价于以下带 my_post_key 的 GET 请求)
curl -s -b acp2.jar "http://localhost/admin/index.php?module=tools-cache&action=rebuild&title=forums&my_post_key=2f366dee58e7d05d421b91c12a3f5593" -o /dev/null -w "rebuild:%{http_code}\n"
mysql -u mybb -pq1w2e3r4 mybbdb -e "SELECT title,LENGTH(cache) AS size_after_acp_rebuild FROM mybb_datacache WHERE title='forums';"

实测回显:

rebuild:302
title	size_after_acp_rebuild
forums	2346

后台重建返回 302 跳转回列表页,缓存行恢复为同样的 2346 字节。ACP 重建与 SQL 直删自动重建等效,区别在于 ACP 可对 settings 这类"非 datacache 存储"的缓存项工作(见 6.4)。

6.4 特殊缓存:settings 存于文件而非 datacache

设置项缓存在 mybb_datacache 里没有对应行(6.1 清单中无 settings),它持久化在 /var/www/mybb/inc/settings.php 文件中。直接改 mybb_settings 表后,该文件不会自动同步。实测用 ACP 重建 settings 缓存并核对文件:

# (ACP Cache Manager 页点击 settings 行 Rebuild Cache,等价于)
curl -s -b acp2.jar "http://localhost/admin/index.php?module=tools-cache&action=rebuild&title=settings&my_post_key=2f366dee58e7d05d421b91c12a3f5593" -o /dev/null -w "rebuild_settings:%{http_code}\n"
# 核对文件头尾与残留
head -3 /var/www/mybb/inc/settings.php
tail -3 /var/www/mybb/inc/settings.php
grep -c "hello_display" /var/www/mybb/inc/settings.php

实测回显:

rebuild_settings:302
<?php
/*********************************\ 
  DO NOT EDIT THIS FILE, PLEASE USE
$settings['woldisplayspiders'] = "1";
$settings['wolorder'] = "username";
0

文件头固定为"DO NOT EDIT"声明(提示此文件由系统维护)、尾行为最后一个设置项,grep 计数 0 表示无已删除插件的残留设置。第八章 8.4 的插件残留清理正是用这一步收尾。

七、计数与统计重建(Recount & Rebuild)

论坛的帖子计数、版块计数、统计数字都是冗余存储的(为列表页性能妥协),删帖、导库、SQL 直改都可能造成与真实数据不同步。ACP 的 Tools & Maintenance → Recount & Rebuild 页面提供 8 项重建动作,实测清单如下:

动作名作用每页处理量默认值
Rebuild Forum Counters重算各版块主题数/帖子数/最后发表50
Rebuild Thread Counters重算各主题回帖数/浏览数/最后回复500
Rebuild Poll Counters重算投票计数500
Recount User Post Counts重算用户发帖数(排除关闭计数的版块)500
Recount User Thread Counts重算用户主题数500
Rebuild Attachment Thumbnails重建附件缩略图—
Recount Statistics重算全站统计(首页/统计页数字)—
Recount Reputation重算用户声望值—

分页默认值实测读取自页面表单(name="forumcounters" value="50" 等),大数据量论坛可调大每页量减少点击次数。

7.1 实测:论坛计数不同步修复

人为制造计数错误,再用后台修复,验证全流程:

# 记录修复前各版块计数基线
mysql -u mybb -pq1w2e3r4 mybbdb -e "SELECT fid,name,threads,posts FROM mybb_forums;"

实测回显:

fid	name	threads	posts
1	My Category	0	0
2	My Forum	0	0
3	General Discussion	1	2
# 模拟事故:把版块 posts 篡改为 999
mysql -u mybb -pq1w2e3r4 mybbdb -e "UPDATE mybb_forums SET posts=999 WHERE fid=1; SELECT fid,name,threads,posts FROM mybb_forums WHERE fid=1;"

实测回显:

fid	name	threads	posts
1	My Category	0	999

后台 Recount & Rebuild → Rebuild Forum Counters 点 Go(表单提交,等价请求如下;my_post_key 为后台会话 CSRF 令牌):

curl -s -b acp2.jar -X POST "http://localhost/admin/index.php?module=tools-recount_rebuild" --data "do_rebuildforumcounters=Go&forumcounters=50&my_post_key=2f366dee58e7d05d421b91c12a3f5593" -o /dev/null -w "rebuild:%{http_code}\n"
# 修复后核对
mysql -u mybb -pq1w2e3r4 mybbdb -e "SELECT fid,name,threads,posts FROM mybb_forums WHERE fid=1;"

实测回显:

rebuild:302
fid	name	threads	posts
1	My Category	0	0

posts 从 999 修复回 0(与基线一致),动作返回 302 跳回列表页。

7.2 实测:全站统计重建

先看统计缓存当前值,再触发 Recount Statistics:

# 重建前:stats 缓存(numposts 为 0,与真实帖子数 2 不符——直改库造成的滞后)
mysql -u mybb -pq1w2e3r4 mybbdb -e "SELECT cache FROM mybb_datacache WHERE title='stats'\G"

实测回显:

*************************** 1. row ***************************
title: stats
cache: a:9:{s:10:"numthreads";s:1:"1";s:20:"numunapprovedthreads";s:1:"0";s:8:"numposts";i:0;s:18:"numunapprovedposts";s:1:"0";s:8:"numusers";s:1:"1";s:15:"numdeletedposts";s:1:"0";s:17:"numdeletedthreads";s:1:"0";s:7:"lastuid";s:1:"1";s:12:"lastusername";s:5:"admin";}
curl -s -b acp2.jar -X POST "http://localhost/admin/index.php?module=tools-recount_rebuild" --data "do_recountstats=Go&my_post_key=2f366dee58e7d05d421b91c12a3f5593" -o /dev/null -w "recount_stats:%{http_code}\n"
mysql -u mybb -pq1w2e3r4 mybbdb -e "SELECT LEFT(cache,120) AS stats_after FROM mybb_datacache WHERE title='stats';"

实测回显:

recount_stats:302
stats_after
a:9:{s:10:"numthreads";s:1:"1";s:20:"numunapprovedthreads";s:1:"0";s:8:"numposts";s:1:"2";s:18:"numunapprovedposts";s:1:

numposts 从 i:0 修正为 s:1:"2"(2 与真实帖子数一致),统计重建生效。恢复备份后建议顺手跑一遍 7.1/7.2 两项。

八、插件管理与冲突排查

MyBB 插件是 inc/plugins/ 目录下的 PHP 文件,激活状态记录在 mybb_datacache 的 plugins 缓存行。插件类故障有三类典型现场:坏插件文件(语法错误)、激活插件的数据表丢失(Fatal 白屏)、插件残留(停用不彻底)。本章用官方示例插件 hello 做全流程实测。

8.1 查看插件目录与激活状态

ls /var/www/mybb/inc/plugins/
# 激活状态(plugins 缓存行,active 数组为空表示无激活插件)
mysql -u mybb -pq1w2e3r4 mybbdb -N -e "SELECT cache FROM mybb_datacache WHERE title='plugins';"

实测回显:

hello.php
index.html
a:1:{s:6:"active";a:0:{}}

后台对应页:Configuration → Plugins(admin/index.php?module=config-plugins),会扫描 inc/plugins/ 全目录列出可激活插件。

8.2 插件激活实测(hello 示例插件)

后台 Plugins 页点击 Hello World! 的 Activate(等价带 my_post_key 的 GET 请求),激活后三处变化实测如下:

curl -s -b acp2.jar "http://localhost/admin/index.php?module=config-plugins&action=activate&plugin=hello&my_post_key=2f366dee58e7d05d421b91c12a3f5593" -o /dev/null -w "activate:%{http_code}\n"
# 变化一:激活态
mysql -u mybb -pq1w2e3r4 mybbdb -N -e "SELECT cache FROM mybb_datacache WHERE title='plugins';"
# 变化二:插件安装的数据表
mysql -u mybb -pq1w2e3r4 mybbdb -e "SHOW TABLES LIKE '%hello%';"
# 变化三:后台操作审计
mysql -u mybb -pq1w2e3r4 mybbdb -e "SELECT uid,action,LEFT(ipaddress,15) AS ip,dateline FROM mybb_adminlog ORDER BY dateline DESC LIMIT 3;"

实测回显:

activate:302
a:1:{s:6:"active";a:1:{s:5:"hello";s:5:"hello";}}
Tables_in_mybbdb (%hello%)
mybb_hello_messages
uid	action	ip	dateline
1	activate		1793370131
1	rebuild		1793370109
1	activate		1793365514

激活即向 plugins 缓存写入 active 数组、创建插件自带的 mybb_hello_messages 表,并在 mybb_adminlog 留下审计记录(激活/重建缓存各一条)。

8.3 坏插件文件:定位与影响面

模拟手误产生一个语法错误的插件文件(少一个右花括号):

cat > /var/www/mybb/inc/plugins/broken_test.php << 'EOF'
<?php
// Broken plugin test
function broken_test_info()
{
	return array("name" => "Broken Test",
		"description" => "plugin with syntax error",
		"website" => "https://example.com",
		"author" => "test",
		"version" => "1.0",
		"compatibility" => "18*"
	);
}
function broken_test_activate()
{
EOF
# 语法检查
php -l /var/www/mybb/inc/plugins/broken_test.php

实测回显:

PHP Parse error:  Unclosed '{' on line 14 in /var/www/mybb/inc/plugins/broken_test.php on line 15
Errors parsing /var/www/mybb/inc/plugins/broken_test.php

坏文件在场时实测各入口的表现:

# 前台:未激活插件不会被加载,不受影响
curl -s -o /dev/null -w "forum:%{http_code}\n" http://localhost/index.php
# ACP 插件列表页:扫描器静默跳过坏文件,页面正常
curl -s -b acp2.jar "http://localhost/admin/index.php?module=config-plugins" -o plug_page.html -w "acp_plugins:%{http_code}\n"
grep -c "Broken Test" plug_page.html
# 但 Apache 错误日志已留下记录
tail -1 /var/log/apache2/mybb_error.log

实测回显:

forum:200
acp_plugins:200
0
[Fri Oct 30 14:22:11.340142 2026] [php:error] [pid 14124] [client 127.0.0.1:45598] PHP Parse error:  Unclosed '{' on line 14 in /var/www/mybb/inc/plugins/broken_test.php on line 15

结论(三条都实测):坏插件文件不会打挂前台(未激活不加载)、不会打挂后台插件页(列表页静默跳过、列表里也不显示),只在 Apache 错误日志留 Parse error。排障抓手是 php -l 与错误日志;处理方式是修复或删除该文件:

rm /var/www/mybb/inc/plugins/broken_test.php
curl -s -o /dev/null -w "forum_after_fix:%{http_code}\n" http://localhost/index.php
curl -s -b acp2.jar "http://localhost/admin/index.php?module=config-plugins" | grep -c "Hello World"

实测回显:

forum_after_fix:200
1

删除坏文件后插件页恢复正常列出 Hello World!(计数 1)。

8.4 事故链实录:激活插件数据表丢失 → 全站 500 → SQL 紧急停用

这是本章核心排障实录,还原"用旧备份恢复了数据库、备份里没有插件的表"这一经典事故。hello 插件处于激活态(8.2),其数据表被误删:

# 事故注入:激活态下删除插件数据表
mysql -u mybb -pq1w2e3r4 mybbdb -e "DROP TABLE mybb_hello_messages; SHOW TABLES LIKE '%hello%';"

实测回显:

Tables_in_mybbdb (%hello%)

(0 行结果 = 表已删除。)前台立即受影响:

curl -s -o /dev/null -w "forum:%{http_code} size:%{size_download}\n" http://localhost/index.php

实测回显:

forum:500 size:0

全站 500、响应 0 字节(白屏)。定位:看 Apache 错误日志(完整一条照抄):

[Fri Oct 30 14:23:25.051847 2026] [php:error] [pid 14053] [client 127.0.0.1:60936] PHP Fatal error:  Uncaught mysqli_sql_exception: Table 'mybbdb.mybb_hello_messages' doesn't exist in /var/www/mybb/inc/db_mysqli.php:340\nStack trace:\n#0 /var/www/mybb/inc/db_mysqli.php(340): mysqli_query()\n#1 /var/www/mybb/inc/db_mysqli.php(801): DB_MySQLi->query()\n#2 /var/www/mybb/inc/plugins/hello.php(473): DB_MySQLi->simple_select()\n#3 /var/www/mybb/inc/class_plugins.php(142): hello_index()\n#4 /var/www/mybb/index.php(26): pluginSystem->run_hooks()\n#5 {main}\n  thrown in /var/www/mybb/inc/db_mysqli.php on line 340

堆栈清晰指认事故链:index.php → pluginSystem->run_hooks() → 激活插件 hello.php(473) 查询了不存在的表。此时 ACP 前台页面同样 500(后台框架也走同一钩子),数据库直改是唯一出路——把 plugins 缓存行的激活数组清空:

# SQL 紧急停用全部插件
mysql -u mybb -pq1w2e3r4 mybbdb -e "UPDATE mybb_datacache SET cache='a:1:{s:6:\"active\";a:0:{}}' WHERE title='plugins';"
mysql -u mybb -pq1w2e3r4 mybbdb -N -e "SELECT cache FROM mybb_datacache WHERE title='plugins';"
# 恢复验证
curl -s -o /dev/null -w "forum_recovered:%{http_code}\n" http://localhost/index.php

实测回显:

a:1:{s:6:"active";a:0:{}}
forum_recovered:200

前台立即恢复 200。紧急停用是绕过 ACP 的救命操作,但属于"暴力"手段——它跳过了插件正常的 _deactivate() 清理逻辑,会留下残留。实测 hello 插件的残留与清理:

# 残留一:插件写入 mybb_settings 的设置项仍在
mysql -u mybb -pq1w2e3r4 mybbdb -e "SELECT sid,name FROM mybb_settings WHERE name LIKE 'hello%';"
# 清理设置项与设置组
mysql -u mybb -pq1w2e3r4 mybbdb -e "DELETE FROM mybb_settings WHERE name LIKE 'hello%'; SELECT ROW_COUNT() AS deleted;"
mysql -u mybb -pq1w2e3r4 mybbdb -e "DELETE FROM mybb_settinggroups WHERE name='hello'; SELECT ROW_COUNT() AS grp_deleted;"
# 复查清零
mysql -u mybb -pq1w2e3r4 mybbdb -N -e "SELECT COUNT(*) FROM mybb_settinggroups WHERE name LIKE '%hello%'; SELECT COUNT(*) FROM mybb_settings WHERE name LIKE 'hello%';"

实测回显:

sid	name
301	hello_display1
302	hello_display2
deleted
2
grp_deleted
1
0
0
# 残留二:inc/settings.php 文件侧同步(按 6.4 用 ACP 重建 settings 缓存)
curl -s -b acp2.jar "http://localhost/admin/index.php?module=tools-cache&action=rebuild&title=settings&my_post_key=2f366dee58e7d05d421b91c12a3f5593" -o /dev/null -w "rebuild_settings:%{http_code}\n"
grep -c "hello_display" /var/www/mybb/inc/settings.php
# 最终状态复核
mysql -u mybb -pq1w2e3r4 mybbdb -N -e "SELECT cache FROM mybb_datacache WHERE title='plugins';"
curl -s -o /dev/null -w "forum_final:%{http_code}\n" http://localhost/index.php

实测回显:

rebuild_settings:302
0
a:1:{s:6:"active";a:0:{}}
forum_final:200

设置项 2 条、设置组 1 条全部清理,settings 文件无残留,论坛运行正常。若插件表仍在只是数据丢失,也可以走另一条路:从插件源码重新执行建表(或后台 Deactivate 再 Activate 让插件自建),不必紧急停用。

九、错误日志定位

9.1 Apache 日志(排障第一现场)

MyBB 站点的 Apache 日志由站点配置指定(ErrorLog ${APACHE_LOG_DIR}/mybb_error.log),实际落在:

ls -lh /var/log/apache2/ | grep -E "mybb|error\.log|access\.log"

实测回显:

-rw-r----- 1 root adm     0 Oct 30 12:11 access.log
-rw-r----- 1 root adm   776 Oct 30 12:12 error.log
-rw-r--r-- 1 root root  20K Oct 30 14:23 mybb_access.log
-rw-r--r-- 1 root root 7.3K Oct 30 14:23 mybb_error.log
-rw-r----- 1 root adm     0 Oct 30 12:11 other_vhosts_access.log

MyBB 站点流量集中在 mybb_access.log / mybb_error.log(站点专属),默认 access.log 为空。定位技巧:

# 看最近的 PHP 错误(Fatal/Parse/Warning 全在此)
tail -20 /var/log/apache2/mybb_error.log
# 按关键字过滤(表不存在类)
grep "doesn't exist" /var/log/apache2/mybb_error.log | tail -3
# 按时间定位:先从 access log 找到出错请求的精确时间点
tail -3 /var/log/apache2/mybb_access.log

实测回显(access log 末 3 行照抄):

127.0.0.1 - - [30/Oct/2026:14:23:49 +0000] "POST /admin/index.php HTTP/1.1" 200 9019 "-" "curl/7.81.0"
127.0.0.1 - - [30/Oct/2026:14:23:49 +0000] "GET /admin/index.php?module=tools-cache HTTP/1.1" 200 18713 "-" "curl/7.81.0"
127.0.0.1 - - [30/Oct/2026:14:23:49 +0000] "GET /admin/index.php?module=tools-cache&action=rebuild&title=settings&my_post_key=2f366dee58e7d05d421b91c12a3f5593 HTTP/1.1" 302 283 "-" "curl/7.81.0"

Fatal/Parse error 的完整样本已在 8.3 与 8.4 实录,日志行包含时间戳、错误级别、出错文件与行号、完整 PHP 堆栈,是 8.4 那类白屏事故的唯一可见线索。

9.2 PHP 错误配置

错误能进 Apache 日志依赖两项 php.ini 配置,实测值:

grep -E "^error_log|^log_errors|^display_errors" /etc/php/8.1/apache2/php.ini

实测回显:

display_errors = Off
log_errors = On

display_errors=Off + log_errors=On 是生产推荐组合:错误只进日志不暴露给访客(8.4 白屏时响应 0 字节正是 display_errors=Off 的表现)。若改成 display_errors=On,Fatal 详情会直接印在页面上——仅限本地调试。

9.3 MyBB 内置日志表(业务审计)

MyBB 把业务动作记录在 6 张专属日志表,行数实测总览:

mysql -u mybb -pq1w2e3r4 mybbdb -e "SELECT (SELECT COUNT(*) FROM mybb_adminlog) adminlog,(SELECT COUNT(*) FROM mybb_moderatorlog) modlog,(SELECT COUNT(*) FROM mybb_tasklog) tasklog,(SELECT COUNT(*) FROM mybb_mailerrors) mailerr,(SELECT COUNT(*) FROM mybb_spamlog) spamlog,(SELECT COUNT(*) FROM mybb_promotionlogs) promolog;"

实测回显:

adminlog	modlog	tasklog	mailerr	spamlog	promolog
10	0	0	0	0	0

各表口径:adminlog(后台管理员操作,本文 8.2/8.4 的激活/重建都留了记录)、moderatorlog(版主操作)、tasklog(计划任务执行历史)、mailerrors(邮件发送失败)、spamlog、promotionlogs。后台对应入口:Tools & Maintenance → Logs 下的 Administrator Log / Moderator Log 等子页。抽查 adminlog 最近记录:

mysql -u mybb -pq1w2e3r4 mybbdb -e "SELECT uid,action,LEFT(data,40) AS data,FROM_UNIXTIME(dateline) AS at FROM mybb_adminlog ORDER BY dateline DESC LIMIT 6;"

实测回显:

uid	action	data	at
1	rebuild	a:1:{i:0;s:8:"settings";}	2026-10-30 14:23:49
1	activate	a:2:{i:0;s:5:"hello";i:1;b:1;}	2026-10-30 14:22:11
1	rebuild	a:1:{i:0;s:6:"forums";}	2026-10-30 14:21:49
1	activate	a:2:{i:0;s:5:"hello";i:1;b:1;}	2026-10-30 13:05:14
1	deactivate	a:2:{i:0;s:5:"hello";i:1;b:0;}	2026-10-30 13:05:14
1	reload	a:1:{i:0;s:7:"plugins";}	2026-10-30 13:02:50

action 列与 8.2/6.3 的实测操作一一对应(activate b:1=激活、b:0=停用、rebuild=缓存重建)。排查"谁动了配置"时这张表是权威依据;日志修剪由计划任务 Log Pruning 按 ACP 设置保留期自动清理。

十、注意事项

  • 先备份后动手:第五~八章所有 UPDATE/DELETE/DROP 前都应有 4.1 的全量备份兜底;恢复演练(4.3)本身也要先出新备份再 DROP。
  • UPDATE 必带 WHERE:MyBB 表都有明确主键(uid/sid/tid/title),批量更新前先 SELECT 同条件核对影响行数。
  • salt 与 password 成对更新:只改 password 不动 salt 是合法的(5.2),但手工拼哈希时盐值取错一个字符整条哈希即失效;推荐 5.3 的一条 UPDATE 让两者同步生成。
  • 紧急停用插件是救命不是常规:8.4 的 SQL 停用会跳过 _deactivate() 清理,事后务必补残留清理(设置项 + 设置组 + settings 文件重建);能进后台时永远优先用 ACP 正常 Deactivate。
  • 恢复旧备份后检查插件表:备份版本若早于某插件安装时间,恢复后该插件表缺失会复现 8.4 的全站 500;恢复后先访问一次前台再交付。
  • 备份文件含全量用户数据与口令哈希:/var/backups/ 下的 dump 应收紧目录权限(仅 root/备份账号可读),避免与站点目录混放。
  • 缓存直改后前台即生效:mybb_datacache 是实时读取的(默认 db 后端无独立文件缓存),SQL 改缓存行后无需重启任何服务;但 settings 缓存例外,在文件 inc/settings.php(6.4)。

十一、常见问题(FAQ)

Q1:按 5.3 重置密码后,登录仍提示用户名密码不正确?

排查三处:① UPDATE 是否真的生效——SELECT salt,password FROM mybb_users WHERE username='admin'; 核对哈希已变;② salt 与哈希是否同源——用 5.1 的核对 SQL 重算 match_ 应为 1;③ 是否触发了登录锁定——检查 mybb_users.loginattempts(或 ACP 登录锁相关设置项),清零后重试。本文实测环境三项全部通过后前台/后台均一次登录成功(5.3/5.4 回显)。

Q2:从备份恢复数据库后,前台打开直接白屏(500)?

对照 8.4 事故链:大概率是备份版本早于某个已激活插件的安装时间、插件的表不在备份里。定位看 tail -20 /var/log/apache2/mybb_error.log 中形如 Table 'mybbdb.mybb_hello_messages' doesn't exist 的行(表名随插件而变),Fatal 堆栈里 inc/plugins/ 目录下的插件文件即肇事插件;用 8.4 的 SQL 紧急停用恢复前台,再决定补表或清理插件。

Q3:恢复后论坛能用,但版块/主题计数和统计数字明显不对?

计数是冗余存储,恢复过程不影响真实数据但可能滞后于备份时点。进 ACP Tools & Maintenance → Recount & Rebuild 依次执行 Rebuild Forum Counters 与 Recount Statistics(7.1/7.2 实测:posts 从手工篡改的 999 修复回 0、统计 numposts 从 0 修正为 2),无需改库。

Q4:后台打不开、怀疑插件冲突,但连插件页都 500?

走数据库路线:SELECT cache FROM mybb_datacache WHERE title='plugins'; 查看 active 数组里激活了哪些插件;按 8.4 清空 active 数组恢复;错误日志 mybb_error.log 里 Fatal 堆栈中 inc/plugins/ 下的文件名与行号可精确指认肇事插件。注意 8.3 的实测结论——坏插件文件(语法错误)只会进日志、不会打挂页面,能打挂页面的是"激活态插件运行时错误"。

Q5:只想备份/恢复单张表怎么办?

mysqldump 支持表级导出与导入:mysqldump -u mybb -pq1w2e3r4 mybbdb mybb_posts > posts.sql 导出,恢复用 mysql -u mybb -pq1w2e3r4 mybbdb < posts.sql(导出文件含 DROP TABLE+CREATE TABLE,导入即整表替换)。注意 4.3 的全量备份文件带 --databases 选项(含 CREATE DATABASE 语句),单表导出不带。

Q6:定时备份任务到点没生成文件?

按序检查:crontab -l 确认条目存在且 % 已转义为 \%(4.2 注意事项);手动执行条目里的命令观察输出(4.2 的 daily 演练即此目的,实测生成 136K 文件);确认 cron 守护进程在跑(systemctl is-active cron 应为 active);检查 /var/log/apache2 之外的系统日志 /var/log/syslog 中 CRON 行有无报错。

十二、参考链接