轻量级 PHP 社区系统(论坛 + 博客),基于自研 SplitDB 纯 SQLite 分片引擎。
版本:V1.0.0 | 兼容 PHP 8.0.2(SQLite 3.33.0)|
编码:UTF-8 without BOM + LF,用于快速了解本程序的全貌与优势。
一、项目概述
FlintHub(原名 SoSite)是一套轻量级 PHP 社区系统,提供论坛(帖子/回复/版块)、博客、用户中心、后台管理、插件系统等完整能力。它最核心的特点是:彻底剥离 MySQL,全部数据由纯 SQLite 分片引擎(SplitDB)承载,从"单库单表"升级为"分片 + 索引 + 缓存"的架构,在保留 PHP 极简部署优势的同时,解决了大数据量下的性能问题。
| 项 | 说明 |
|---|---|
| 项目定位 | 轻量级 PHP 社区系统(论坛 + 博客一体) |
| 当前版本 | FlintHub V1.0.0(由 SoSite 5.0 MySQL 版重构而来) |
| 运行环境 | PHP 8.0.2(SQLite 3.33.0),Nginx / Apache / phpstudy 均可 |
| 数据引擎 | SplitDB —— 纯 SQLite 分片引擎(无任何 MySQL 依赖) |
| 数据规模实测 | 5 万用户、19.2 万帖、16 万回复、235 万搜索索引行 |
| 目录约定 | 站点根目录为可运行骨架;update/ 为同步镜像 + 全部文档 |
演进脉络:SoSite 5.0(MySQL 单库)→ 重构为 SoSite 1.0(SplitDB 纯 SQLite 分片引擎)→ 品牌重命名 FlintHub。
二、核心架构:SplitDB 纯 SQLite 分片引擎
2.1 数据分层
data/
├── meta/ # 元数据层(SQLite 库)
│ ├── business.sqlite # 非分片业务:用户/版块/设置/博客/私信/附件/标签/投票
│ ├── main_index.sqlite # 帖子主索引 topic_index(19.2万行) + 回复定位索引 reply_index(16万行)
│ ├── search/ # 搜索索引季度分文件 search_{YYYYQn}.sqlite(4 文件,235 万行)
│ ├── global_id.sqlite # 全局 ID 生成器(topic/reply 原子取号游标)
│ ├── sessions.sqlite # 会话存储(SQLite handler)
│ └── task_queue/queue_{0..2}.sqlite # 异步任务队列(三队列打散锁竞争)
├── bucket/active/{季度}/{0..31}.sqlite # 帖子/回复真相源(季度时序 + ID2 哈希分桶)
├── bucket/archive/ # 18 个月以上冷数据归档桶
└── extern/{年}/{季}/{桶}/ # 正文外置:存量 .bin/.idx 偏移索引,新帖 .txt(读取自动回退)
2.2 分片与路由规则(ShardRouter)
- 一级分区:季度时序分区(
YYYYQn,如 2026Q3),新数据写入当前季度; - 二级分区:哈希分桶(默认 32 桶),写入路由 =
ID % 桶数量; - 读取永远以
main_index.bucket_path为准,不再二次哈希;未来季度可平滑升级 32 → 64 → 128 桶,历史数据零迁移。
2.3 写入与读取链路
写入 = global_id 原子取号 → 季度分区 + ID2 哈希 → extern 正文原子写(tmp+rename)
→ 桶 topic/reply 真相源 → main_index 索引行(最终一致性,队列可选)
读取 = 列表/分页走 main_index(索引行含 bucket_path)→ 详情读桶 + extern(.bin/.idx 优先,缺失回退 .txt)
搜索 = 自研 bigram 倒排索引(季度分文件,SearchIndexStore)
2.4 引擎关键组件(app/SplitDB/)
| 组件 | 职责 | 亮点 |
|---|---|---|
DBFactory |
SQLite 连接池 | 单进程 LRU 最大 8 句柄,统一 6 条 PRAGMA(WAL/busy_timeout),禁持久连接 |
IDGenerator |
全局 ID 生成 | BEGIN IMMEDIATE 写锁事务原子取号(兼容无 RETURNING 的 SQLite 3.33) |
ShardRouter |
分片路由 | 季度 + 哈希双级分区,路径推导唯一依据 |
ExternStorage |
正文外置存储 | tmp+rename 原子写;存量 .bin/.idx 偏移索引(fseek 定位,懒加载);编辑后作废 .idx 条目回退 .txt(修复虚拟主机编辑不生效) |
Queue / QueueConsumer |
异步任务队列 | 三队列打散锁竞争;CAS 抢占/超时恢复/重试上限;sync/cron/cli 三模式 |
Schema |
初始化与种子 | 幂等 bootstrap(目录树 + 8 库 + 22 表 + 默认种子) |
ViewCounter |
浏览计数 | APCu 内存计数 + shutdown 批量落库;无 APCu 静默降级为直写 UPDATE(不崩站) |
SearchIndexStore |
搜索索引 | 季度分文件存储,与 business 库物理隔离(P21 瘦身 306MB → 19MB) |
2.5 事务与一致性
- 桶写入用原始 SQL
exec('BEGIN')/exec('COMMIT');business 库用beginTransaction()/commit(),二者不混用; - 正文外置红线:先原子写 extern → 再写索引行,绝不允许"索引导入成功但正文丢失"(迁移故障注入已验证);
- 计数延迟合并:统计/浏览数先走内存/APCu 计数,请求结束时批量写回,把写放大钉死在量化边界。
三、性能实测优势
FlintHub 在 19.2 万帖、5 万用户、235 万搜索索引行的真实数据规模下,经过 P22~P25 系列优化,实测数据如下(均来自真实环境逐查询剖析):
3.1 首页加载:0.11s → 缓存命中 2.3ms
| 问题 | 根因 | 修复 | 实测效果 |
|---|---|---|---|
| 首页 0.11s | 统计 GROUP BY 对 19.2 万行全表扫描(60s TTL 周期性触发 ~99ms) | 永久化统计缓存(去 TTL,仅发帖/回帖失效) | 99ms → 2.14ms |
| 后台首页 users 排序 | ORDER BY created_at 无索引 → 全表扫描+排序 |
新增 idx_users_created |
26.5ms → 0.05ms |
| 在线人数统计 | 无 last_activity 索引 + 无缓存 | 新增索引 + 30s 短缓存 | 5.94ms → 0.10ms |
| 深翻页(第 12000 页) | LIMIT n OFFSET m 线性跳过索引项 |
keyset 行值比较(游标化) | 188ms → 0.36ms |
| 编辑帖子不生效(虚拟主机) | 读取链 .bin/.idx 优先,编辑只重写 .txt,旧偏移遮蔽新正文 | invalidateArchiveEntry() 作废 .idx 条目回退 .txt |
往返实测通过 |
| 浏览数恒为 0 | APCu 只内存计数,无落库调用点 | shutdown 挂接 ViewCounter::flush() |
0 → 实时累积 |
3.2 缓存体系(读降级为拼缓存)
| 缓存层 | 机制 | 命中成本 |
|---|---|---|
| 入口前置缓存(P23/P24) | index.php 在 require init.php 之前直接输出 pages/ 缓存并 exit;纯游客 / 有会话 Cookie 但未登录的游客均可命中 | ~2.3ms(keep-alive),跳过 Session/DB/插件/在线状态全部引导 |
| 页面永久缓存(PageCache) | data/runtime/pages/*.html 整页静态化,无 TTL,发帖/回帖主动失效 |
命中直出 |
| 统计永久缓存 | Category::getCategoryStats() 聚合缓存(GROUP BY 一次出全部版块数) |
2ms 级 |
| 版块最新帖快照(P25,Ofstar 借鉴) | category_{id}_latest.json,发帖/回帖刷新 |
零大表查询 |
| 在线数据短缓存 | online_data.cache.json 30s TTL |
7.3ms → 缓存命中 |
| 运行时 KV 缓存 | Settings 三层缓存(内存 → 文件 → DB) | 亚毫秒 |
3.3 性能设计原则(踩坑教训沉淀)
- ⚠️ 给统计查询加覆盖索引前必须 EXPLAIN 验证列表查询的规划器选择:曾因 SQLite 误选统计覆盖索引导致 19 万行 TEMP B-TREE 排序(0.1s → 0.3s 回归),已回滚——优先用永久缓存而不是新索引;
- 写放大硬上限(Ofstar 借鉴):online_users 清理、Session GC 均 LIMIT 5000 分批,防止单次请求卡顿;
- keyset 深翻页:仅"上一页/下一页"走游标快路径,页码数字直达走 OFFSET(低频可接受),置顶帖纳入行值比较不重不漏;
- CLI 测量注意:跨进程 warm 渲染有 7~66ms 波动(冷 WAL/杀软扫描),测耗时必须同进程多次取稳态。
四、功能特性
4.1 前台功能
- 论坛:多版块、发帖/回帖、置顶/精华/高亮、颜色标记、Markdown 编辑器、附件上传(权限随版块)、软删/回收站、深翻页(keyset 游标);
- 博客:分类、发布、评论、管理;
- 用户中心:注册/登录(邮箱验证 + 记住我)、个人资料/头像/签名、私信(收件/发件)、积分/等级、用户组与权限;
- 搜索:自研 bigram 倒排索引,季度分文件存储,支持时间范围过滤;
- 标签系统:帖子标签、标签页聚合;
- 投票与 API:帖子投票、图片抓取、验证码、Ajax 上传(htmx + Alpine 前端,
fhajax.js/fhstate.js); - 主题切换:单模板体系(modern 主题 + 配色变体),后台可传 CSS/切换默认主题。
4.2 后台管理
- 仪表盘:站点统计、环境信息、在线数据(30s 缓存);
- 用户管理:列表分页、按关键词/字段/角色/状态查询、编辑(角色/积分/等级/用户组,含唯一管理员保护)、删除级联;
- 帖子管理:列表分页、按分类筛选、批量删除/批量移动(操作后保留筛选)、编辑、删除;回收站(Trashed)恢复;
- 版块/博客分类:增删改、排序、删除重置;
- 标签/等级/用户组/权限:完整后台管理;
- 设置:基本信息/显示/邮件(SMTP AES 加密)/搜索/任务调度(三模式 + 队列面板 + 手动触发);
- 数据库维护:SQLite 信息、备份、VACUUM、索引管理;
- 邮件验证、主题管理、插件管理(激活/停用/卸载/编辑)。
4.3 插件系统(26 个,23 个独立数据库)
FlintHub 插件按「一目录一插件」组织于 plugins/,共 26 个(今日热榜 news_aggregator 不在其列)。其中 23 个插件的数据独立存放于自身目录 plugins/{名}/data/{名}.sqlite,互不依赖、卸载即删,核心库不受插件建表影响;另有 3 个无库插件:本地草稿保护 draft_saver(纯前端 localStorage)、代码仓库 code_repo(仓库目录直读)、图标生成器 icon_generator(生成静态图标资源)。
按插件后台分类列举如下:
- 社区类(11 个):AI 助手
ai_assistant| 内容审核content_review| 楼中楼floor_reply| 版块强制 Tagforum_required_tag| 邀请注册invite| 社区治理mod_system(举报中心 + 小黑屋 + 通知联动) | 通知中心notifications| 帖子收藏post_favorite| 问答社区qa| 任务中心task_center| 用户主页user_profile - 内容类(4 个):站点公告
announcements| 编辑信息edit_info| 友情链接friend_links| 单页/链接管理single_page - 娱乐类(6 个):每日签到
daily_checkin| 掷骰子dice| 勋章中心medal| 积分商城points_mall| 猜题对决quiz_duel| 积分红包red_packet - 系统类(2 个):代码审计
code_audit| SEO 优化seo
插件通过 Plugin::hook() 钩子系统与核心联动(核心侧实测 37 处调用点,覆盖控制器/视图/init 全链路),启用经 activate/deactivate 完成建表与清理,无致命缺索引联表查询。
五、部署形态与兼容性
5.1 三种队列调度模式(后台可切换)
| 模式 | 适用场景 | 行为 |
|---|---|---|
| sync 同步直写(默认) | 个人测试 / 廉价虚拟主机 | 发帖/回帖成功后页面等待索引与统计更新完毕再返回,数据 100% 实时 |
| cron 定时触发 | 有 Crontab 权限但无法常驻 PHP 进程 | 发帖后任务入 SQLite 队列,由 cli/worker.php --once 或 cron_trigger.php(web 入口,带文件锁防并发)批量消费 |
| cli 常驻消费 | Linux VPS / 独立服务器(Supervisor) | 后台常驻 cli/worker.php 实时消费,页面响应最快 |
5.2 虚拟主机适配(无 SSH / 无 APCu 环境)
- 无 APCu 自动降级:浏览计数、extern 正文缓存静默降级为直写/直读,不崩站(白皮书红线要求);
- 无 crontab:可用
cron_trigger.phpweb 触发(仅 cron 模式生效,文件锁防重入); - 编辑不生效问题已修复:虚拟主机场景 .idx 旧偏移遮蔽新正文 →
invalidateArchiveEntry()回退 .txt; - 纯 SQLite 部署:无需 MySQL、无需安装扩展(PDO_SQLITE 内置),
data/目录可写即可。
5.3 环境要求
| 项 | 要求 |
|---|---|
| PHP | ≥ 8.0(SQLite 3.33+,实测 8.0.2) |
| Web 服务器 | Nginx / Apache / php -S 均可(附 nginx-server.conf) |
| 扩展 | PDO_SQLITE(内置);APCu 可选(有则更快,无则降级) |
| 安装 | 浏览器访问 install.php → 环境检测 → 建管理员 → 完成 |
六、工程质量与安全
6.1 迁移与数据安全红线
- 正文剥离红线:旧 MySQL → SplitDB 迁移时正文 MEDIUMTEXT 必须成功剥离为 extern/ 文件;失败则整体回滚,绝不允许「索引导入成功但正文丢失」(故障注入已验证);
- 迁移顺序红线:先原子写 extern → 再写索引行,三处落盘(extern + 桶 + 索引)完整性校验;
- SQLite 变量上限 32766:批量 IN/NOT IN 必须 array_chunk 分批(900/批安全),NOT IN 分批先算孤儿集防误删全表。
6.2 安全防护
| 层 | 措施 |
|---|---|
| SQL 注入 | 全站参数绑定 + Model 层 buildWhere 白名单字符校验 + ORDER BY sanitize |
| CSRF | 前后台表单 Token 校验(Csrf helper) |
| XSS | 视图输出统一 $this->e() 转义 |
| 会话 | SQLite session handler + 独立连接防游标冲突 + session_regenerate 防固定 + cookie_secure/samesite |
| 上传 | 扩展名白名单 + 5MB 上限 + 权限随版块 |
| 后台 | 管理员二次密码验证(30 分钟超时)+ 唯一管理员降级/删除保护 |
| 审计 | AuditLog 全链路记录(删除/移动/设置等管理操作) |
6.3 自动化测试(冒烟)
cli/smoke_install.php:空目录安装全链路,30/30 通过;cli/smoke_migrate.php:迁移全链路(含红线故障注入),21/21 通过;- 引擎冒烟:DBFactory / IDGenerator / ShardRouter / ExternStorage / Queue / Schema / ViewCounter 各独立脚本;
- 全部交付文件
php -l校验 +update/镜像fc /b逐字节比对。
6.4 已知取舍与注意事项
- 浏览数为 0 是历史数据(旧库从未落库),修复后新浏览量实时累积;回填需另找数据源;
- 改视图后必须失效
data/runtime/pages/*.html静态缓存(发帖/回帖自动失效,视图改动本身不会); - 前置缓存仅游客命中,已登录用户走完整管线(个性化内容不缓存);
MAIL_PASS_KEY为 AES 密钥(含历史 SoSite 字样),勿改(改则已存 SMTP 密码无法解密)。
本文档完。FlintHub 1.0 —— 用纯 SQLite 分片引擎实现 PHP 社区系统的极致轻量与可靠性能。