本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:暂存20260913/docs/SplitDB引擎控制台使用说明 (2).md。
SplitDB 控制台 — 使用说明
版本:FlintHub 1.0(P30 拆分重构 + P41 智能扩容与归档)| 后台路径:
/admin/database/overview(数据管理 → SplitDB 概览)与/admin/database/settings(数据管理 → SplitDB 设置)前置条件:管理员登录 + 30 分钟二次密码验证(与既有后台一致)
配套文档:
update/SplitDB控制台拆分与操作日志改造计划.md(P30 设计与决策)、update/SplitDB引擎控制台改造计划.md(P29 原始设计)、update/智能扩容与归档改造计划.md(P41 设计与决策)
一、核心原理(必读:扩容零迁移)
桶数增加后旧数据原地不动,不进行任何数据迁移或重哈希:
| 场景 | 路由规则 | 说明 |
|---|---|---|
| 写入新帖(扩容后) | 新增桶编号范围 SPLITDB_PREV_BUCKET_SIZE + (id % (SPLITDB_BUCKET_SIZE - PREV)) | [P41/D3] 扩容后所有新主题帖全部进入新增桶范围(32→64 后新帖进桶 32..63),旧桶不再接收任何新主题帖;判定依据 SPLITDB_LAST_EXPANSION_AT(帖子创建时间 ≥ 扩容时间戳) |
| 写入新帖(未扩容/历史导入) | id % SPLITDB_BUCKET_SIZE | 从未扩容(常量 0)或历史帖导入(created_at 早于扩容时间戳)回退旧哈希,与旧行为完全一致 |
| 写入回复 | 跟随父帖 bucket_path | 旧帖的回复和编辑仍留在原桶,不受路由影响 |
| 读取 | 以 main_index.bucket_path 为唯一依据 | 不重新计算哈希,旧帖永远留在旧桶,读取不受影响 |
- 32 → 64 桶后:旧帖继续留在旧桶(冻结只收回复),新帖进入新增桶范围;零停机、零搬家、零迁移脚本;
- 桶数只增不减(32 → 64 → 128 → 256),历史数据零迁移(白皮书 5.2 既定设计);
- 扩容动作由
updateBucketSize()统一落盘三个常量:SPLITDB_BUCKET_SIZE/SPLITDB_LAST_EXPANSION_AT(时间戳)/SPLITDB_PREV_BUCKET_SIZE(扩容前桶数)。
二、后台菜单结构(P30 重组)
数据管理
├─ SplitDB 概览 → /admin/database/overview (只读监控)
├─ SplitDB 设置 → /admin/database/settings (操作型)
└─ 备份优化 → /admin/database (备份/VACUUM/恢复)
站长工具
├─ 操作日志 → /admin/logs/audit (审计日志查看)
└─ 维护工具 → /admin/maintenance旧路径
/admin/database/engine已 302 重定向至/admin/database/overview,旧链接不失效。
三、SplitDB 概览(只读监控)
| 数据项 | 含义 |
|---|---|
| 当前配置桶数 | config.php 中 SPLITDB_BUCKET_SIZE 的当前值(文件优先) |
| 当前季度 | ShardRouter::quarter(),如 2026Q3 |
| 分片分布统计 | 当前季度 bucket/active/{季度}/{0..N}.sqlite 各桶 topic/reply 行数与文件大小 |
| 当前总帖子数 | topic_index 全量 COUNT |
| 建议桶数 | <10万→32;10万~50万(含50万)→64;50万~200万(含200万)→128;>200万→256 |
- 分片统计带 60s 控制台缓存(
data/runtime/cache_engine_stats.json),避免 128/256 桶 × 多季度时页面退化; - 点「🔄 刷新统计」强制实时重算并回写缓存;
- 单桶读取异常显示「读取异常」并跳过,不阻塞整页;
- 本页纯只读、无 POST 操作;所有操作型功能在「SplitDB 设置」页。
四、SplitDB 设置(操作型)
① 桶数动态调整(核心操作)
- 下拉选择目标桶数(32 / 64 / 128 / 256,仅列出 2 的幂次方);
- 点「更新桶数」→ 确认弹窗 → 服务端校验:必须 ≥ 当前桶数(只允许增加,含白名单/只增不减双层兜底);
- 写 config.php 安全四步:备份(
protected/backups/config.php.bak.{时间戳}.{随机})→ 正则替换常量行 →file_put_contents(LOCK_EX)→opcache_invalidate(绝对路径); - 成功提示:"桶数已更新。新增数据将自动按新规则路由。旧数据不受影响。";
- 若 config.php 不可写(严格权限 VPS 的 0444/0644)→ 按钮置灰 + 提示「配置文件 config.php 不可写,请检查文件权限」。
⚠️ 改 config 生效时机:当前请求仍用旧常量,新值从下一请求起生效——属预期行为。
② 自动扩容设置(可选)
| 项 | 说明 |
|---|---|
| 开关 | 开启后由系统自动检测并扩容 |
| 触发阈值 | [P41] 数据量最大的桶的 topic 行数 ≥ 单桶容量(bucket_safe_capacity,见 ③)时触发(原"总帖数超阈值"口径已废弃,阈值改由「单桶容量」区块配置) |
| 只读参考 | 「当前总帖子数」仅作参考展示 |
| 24h 间隔 | 距上次扩容不足 24 小时不执行(auto_expand_last_at 记录),防频繁扩容 |
| 防重入 | 写 config 前强制 flock(data/lock/auto_expand.lock),获取失败立即放弃,防并发写坏/跳级扩容 |
触发点(双触发):
- SplitDB 设置页加载时(GET);
cron_trigger.php(web 触发)与cli/worker.php(常驻空闲回调,静态 60s 节流)消费末尾——无需管理员访问也能自动扩。
扩容动作:桶数 ×2(上限 256)→ 写 config(同安全四步,同步落盘 SPLITDB_LAST_EXPANSION_AT + SPLITDB_PREV_BUCKET_SIZE)→ AuditLog::log('auto_expand', ...) 记录 → 更新 auto_expand_last_at。
③ 单桶容量(智能扩容阈值,[P41])
- 选项:3 万 / 5 万 / 8 万 / 10 万(下拉框),默认 5 万;提示语:"该值决定每个桶在触发扩容前可容纳的主题帖数量。若服务器内存较高(如 4GB 以上),可适当调高;若在低配环境(如 600M 空间),建议保持 3~5 万。"
- 口径(D1):桶负载 = 仅该桶
topic行数(主题帖数),回复不计入; - 触发规则联动:
- 最大桶负载 ≥ 100% 容量 → 自动扩容(需 ② 开关开启);
- 最大桶负载 ≥ 80% → 站点设置页顶部显示"⚠️ 建议扩容"提示条(D5);
- 回复写入前,父帖所在桶负载 ≥ 90% → 紧急扩容(不受开关限制,24h 守卫 + flock 保护);
- 回复写入前,父帖所在桶负载 ≥ 100% → 拒绝写入,提示"当前桶已满,请联系管理员扩容"。
④ 旧帖归档设置([P41])
- 选项:关闭归档(0)/ 180 天 / 365 天 / 自定义(数字输入,0~3650),默认 365 天;
- 超过设定天数的帖子自动标记
topic_index.is_archived = 1,进入归档只读状态: - 前台详情页顶部显示"该帖已归档(发布于 XXX 天前),无法回复或编辑"横幅;
- 非管理员不显示回复框、不可回复/编辑;内容仍可完整查看,引用链接可手动粘贴到新帖;
- 管理员豁免:可继续回复和编辑归档帖(服务端强制,非仅前端隐藏);
- 标记方式(双保险):
- 每日定时脚本
cli/archive_mark.php(分批 5000 条/批,幂等,--dry-run预览)——由cron_trigger.php(web 可触发,适合无 crontab 的虚拟主机)或服务器 crontab 调用; - 读取时惰性标记:详情页发现超期未归档帖时顺手置位,定时任务未跑也能即时生效。
⑤ 扩容记录
- 本页展示 最近 10 条 扩容/调整记录(来源:
audit_logs中 action ∈ {auto_expand, admin_action, emergency_expand} 且 target_type='database'),显示时间/操作人/类型/详情/IP; - 完整历史请查看「站长工具 → 操作日志」页(可筛选日期与操作类型)。
⑥ 一致性检查(诊断工具)
- 选择检查范围:「当前季度(默认)/ 全部历史」;
- 点「开始检查」→ 遍历范围内全部桶,逐桶 COUNT topic/reply 汇总;
- 对照
topic_index/reply_index全量行数:
- 全部历史:严格对拍,不一致行红色高亮「⚠️ 不一致」;
- 当前季度:索引为全量,结果仅作参考对照(标注说明);
- 单桶读取异常在页面底部红色提示桶文件清单。
五、操作日志页(/admin/logs/audit)
- 功能:分页列出
audit_logs表记录,显示 时间 / 管理员 / 操作类型 / 目标 / IP / 详情(详情超长截断,悬停查看全文); - 筛选:GET 表单,支持按操作类型(下拉)与日期(YYYY-MM-DD)筛选;分页条带参不丢条件;「清除筛选」一键还原;
- 操作类型下拉:从
audit_logs表DISTINCT action动态读取(新操作类型自动出现,免维护)+ 60s 文件缓存(data/runtime/cache_audit_actions.json),实测覆盖索引亚毫秒级; - 权限:仅管理员(need_admin,BaseController 构造器 + 30 分钟二次验证);
- 系统任务(如自动扩容)无操作人时管理员列显示
-。
六、备份与优化(/admin/database)
| 按钮 | 行为 |
|---|---|
| 立即备份 | 全 data/ 目录 WAL checkpoint 后打包为 forum_backup_{时间戳}.zip |
| VACUUM 优化 | 全部 SQLite 库 VACUUM + WAL checkpoint(大库耗时,建议低峰期) |
| 上传并恢复 | 上传 .zip 整包(还原 data/)或 .sqlite 单库(按命名规范映射),带路径穿越防护 |
| 备份列表 | 文件名 / 大小 / 时间 / 下载 / 恢复 / 删除(分页) |
七、安全设计要点
- 全部 POST 走
Csrf::verifyOrDie;桶数白名单 [32,64,128,256];阈值 intval;筛选参数白名单(action/^[a-z_]{1,32}$/、date/^\d{4}-\d{2}-\d{2}$/);备份文件名basename清洗 + 路径穿越防护; - 写 config 前先备份;写失败不覆盖原文件并提示;
opcache_invalidate(realpath(...))传绝对路径; - 分片统计逐桶直接 PDO(不经 DBFactory 池,避免 LRU 8 句柄抖动)+
PRAGMA query_only只读句柄 + 读完即关; - 自动扩容 24h 守卫 + flock 防重入 + AuditLog 全记录;worker 常驻 60s 静态节流防死循环打 DB;
AuditLog::query()日期筛选参数绑定(LIKE 前缀命中idx_audit_logs_created索引);- 仅管理员可访问全部后台页面(BaseController 构造器校验)。
八、维护命令速查
# 语法检查(改动文件)
php -l app/Controllers/Admin/DatabaseController.php
php -l app/Controllers/Admin/LogController.php
php -l app/Views/admin/database/overview.php
php -l app/Views/admin/database/settings.php
php -l app/Views/admin/logs/audit.php
php -l app/Helpers/AuditLog.php
php -l app/Views/admin/_sidebar.php
# 手动触发自动扩容(cron 模式 / CLI)
php cli/worker.php 0 --once # --once 末尾会自动调用 maybeAutoExpand
php cron_trigger.php # 仅 queue_mode=cron 时生效;顺带执行每日归档标记
# 旧帖归档标记(每日定时,幂等可重跑)
php cli/archive_mark.php # 正式标记超期帖
php cli/archive_mark.php --dry-run # 仅预览待标记行数
# 查看审计日志
# 后台 → 站长工具 → 操作日志(/admin/logs/audit)
# 清除引擎统计缓存 / 操作类型缓存(异常时手动)
# 删除 data/runtime/cache_engine_stats.json 与 data/runtime/cache_audit_actions.json 即可(60s 自动过期)九、常见问题(FAQ)
| 问题 | 解答 |
|---|---|
| 扩容后旧帖找不到? | 不会。旧帖读取以 main_index.bucket_path 定位,与桶数无关;扩容只影响新帖路由 |
| 扩容后新帖进哪些桶? | [P41/D3] 扩容后新主题帖全部进入新增桶编号范围(如 32→64 后进桶 32..63),旧桶冻结不再接收新主题帖(回复仍进旧桶) |
| 想从 64 缩回 32? | 不允许(服务端强制只增不减)。缩桶需要重哈希迁移,违背零迁移设计 |
| 自动扩容为什么没触发? | 检查:① 开关是否开启 ② 数据量最大的桶的 topic 行数是否 ≥ 单桶容量 ③ 距上次扩容是否 <24h ④ 是否有其他扩容在进行(flock 被占) |
| 建议扩容提示在哪? | [P41/D5] 站点设置页(/admin/settings/basic)顶部,最大桶负载 ≥ 容量 80% 时显示"⚠️ 建议扩容" |
| 归档帖还能回复/编辑吗? | 非管理员不能(详情页不显示回复框,服务端同样拦截);管理员豁免可继续回复和编辑 |
| 归档时间改了多久生效? | 每日定时脚本(cron_trigger 或 crontab)+ 详情页读取时惰性标记双保险,最迟一次访问即生效 |
| 归档标记跑得很慢? | 分批 5000 条/批上限(写放大红线),大库需多批;--dry-run 可先预览规模 |
| 概览页加载很慢? | 分片统计有 60s 缓存;若首次加载慢属正常(全桶逐文件 COUNT);「全部历史」一致性检查耗时更长属预期 |
| 桶数改了但新帖没进新桶? | 确认保存成功提示出现;PHP-FPM 下一请求才加载新常量(opcache 已失效);可查看 config.php 中 SPLITDB_BUCKET_SIZE 的值确认 |
| config.php 不可写? | 检查文件权限(chmod 664/666 或赋予 Web 用户写权限),按钮置灰时无法调整桶数 |
| 旧链接 /admin/database/engine 还能用吗? | 能。已 302 重定向至 /admin/database/overview |
| 操作日志看不到某类操作? | 操作类型下拉来自 audit_logs 表 DISTINCT action(60s 缓存),新类型最迟 60s 后出现;空结果请核对日期/类型筛选 |
使用说明完。P30 拆分设计详见 update/SplitDB控制台拆分与操作日志改造计划.md;P29 原始设计详见 update/SplitDB引擎控制台改造计划.md。