[开发]SoSite 1.0(SplitDB 引擎迁移)— 改造执行计划

👑Lv.11 元老 🌞 骨干会员
2026-10-24 00:12:49

本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:暂存20260913/docs/改造执行计划 (2).md。

SoSite 1.0(SplitDB 引擎迁移)— 改造执行计划

文档状态:定稿(2026-08-13,经用户确认)

基线版本:SoSite 5.0(MySQL)→ SoSite 1.0(SplitDB 纯 SQLite 分片引擎)

依据:《SplitDB v2.3 白皮书》(根目录 Splitdb V2.3 白皮书.txt)

兼容基线:PHP 8.0.2(phpstudy D:\phpstudy_pro\Extensions\php\php8.0.2nts,内置 SQLite 3.33.0)

编码规范:全部生成文件 UTF-8 without BOM + LF


一、角色与目标

  • 角色:资深 PHP 架构师与重构专家
  • 目标:将现有 SoSite 5.0(MySQL)核心代码重构为 SoSite 1.0(SplitDB 纯 SQLite 分片引擎)
  • 原则:彻底剥离 MySQL,不使用任何 MySQL 兼容层 / 双引擎切换 / 回退功能,只存在 SplitDB 一种驱动

二、核心开发红线(严禁违反)

  1. 彻底移除 MySQL 依赖:所有数据读写必须通过 SQLite 文件进行。严禁保留 PDO_MySQL 连接逻辑、mysql_* 函数、MySQL 语法兼容判断
  2. 不兼容 MySQL:不提供双引擎切换或 MySQL 回退,只存在 SplitDB 一种驱动
  3. 功能精简:保留除插件之外的核心功能(用户注册/登录、发帖、回帖、版块、个人中心等);插件系统代码与目录全部保留,不删除——P0~P6 阶段仅「暂停迁移插件」,保持插件未激活/忽略状态,先让 SoSite 核心骨架跑通 SplitDB

补充要求(用户 2026-08-13 追加,与红线同等强制)

  1. 【精确的"暂不删除"机制】(P3/P5):遇到 Plugin::hook() 调用时,严禁直接删除该行代码。必须采用 // [SplitDB] 临时注释: 前缀将其注释掉,注释内保留原始调用原文,方便日后回顾与恢复(恢复时去掉注释前缀即可)。
  2. 【数据无损迁移】(P7 重点):编写 cli/migrate.php 时,必须确保正文(MEDIUMTEXT)成功剥离为 extern/ 目录下的 .txt 文件。若 extern 文件生成失败,迁移脚本必须报错并整体回滚——严禁出现「索引导入成功但正文丢失」的情况。
  3. 【ViewCounter 安全降级】(P4):实现浏览计数时,若系统检测到未安装 APCu 扩展,严禁让全站报错。必须静默降级为简单的 UPDATE 实时计数(降低性能但不崩站),确保程序在任何 PHP 环境下都能运行。

三、执行规范与工作流(必须严格遵守)

  1. 计划先行,禁止直接修改:行动前先输出书面《改造执行计划表》,经用户确认后才动工
  2. 绝对安全的更新模式:改动的核心文件可在原位置直接覆盖,同时在 update/ 目录下同步改动过的文件;update/ 根目录维护 CHANGELOG.md,详细记录修改了哪些文件、进行了什么逻辑变更
  3. 串行执行与单步反馈:不批量修改,严格"改好一个核心模块,立即测试,通过后再进行下一个"
  4. 编码安全:全部文件 UTF-8 without BOM、LF 行尾,禁止乱码

四、架构与实现要求(参照白皮书 v2.3)

#要求对应实现
1分片结构季度时序分区 + 32 桶哈希分片(ID % 桶数),读取以 main_index.bucket_path 为准,不二次哈希
2外置文件存储帖子/回复正文剥离 SQLite,存入 extern/ 目录 .txt 文件
3连接池与并发单进程 LRU 连接池,最大 8 个常驻句柄,禁止 ATTR_PERSISTENT 持久连接
4全局 ID 生成器global_id.sqlite 表,高并发原子自增取号(含重试机制)
5后台任务队列SQLite task_queue 异步队列(异步重建索引/更新统计)
6索引缓存列表页索引 main_index.sqlite 与核心 Bucket 存储分离,支持最终一致性

五、适配 SoSite 5.0 现有骨架

  1. Model 层:保留 app/Models/ 目录结构(User.php、Thread.php、Post.php 等),查询逻辑重写为调用 SplitDB 连接池与路由,而非直接执行 MySQL SQL
  2. Config:修改 config.php,去掉 MySQL 配置项,加入 DATA_PATH(数据目录)、BUCKET_SIZE(桶数量)等 SplitDB 配置
  3. 新增核心 Helper:在 app/Helpers/(实际落点 app/SplitDB/)为 SplitDB 功能(外置文件读写、队列任务分发等)增加辅助类

六、交付标准

  1. 结构完整性:update/ 目录代码必须是"可以运行"的骨架
  2. 不可逆性:提供可执行的旧 MySQL → SplitDB 数据迁移脚本(可选)
  3. 测试流程:交付后提供本地手动测试说明(如何发帖并验证分片文件生成)

七、勘察结论(现状盘点)

项现状对迁移的影响
config.php不存在,由 install.php 生成(DB_HOST/DB_PORT/DB_NAME/DB_USER/DB_PASS/DB_CHARSET + 上传配置)重写生成模板:去 MySQL 配置,加 DATA_PATH/BUCKET_SIZE 等
app/Core/Database.phpMySQL 单例 PDO,query/fetchAll/fetchOne/lastInsertId/begin/commit/rollback + 独立 Session 连接重写为 SplitDB 门面,保留同名 API,Models 改动最小化
app/Core/Model.php通用 CRUD,反引号 + LIMIT + 参数绑定(MySQL 语法)去反引号、SQLite 兼容,API 不变
app/Models/*(10 个)直接拼 MySQL SQL,含 GREATEST()/NOW()/ON DUPLICATE KEY 等全部重写为 SQLite 语法 + 分片路由
protected/database/schema.sqlMySQL DDL(ENGINE=InnoDB/AUTO_INCREMENT/MEDIUMTEXT)重写为 SQLite DDL(meta 库 + 分片表)
app/init.phpSession 存 MySQL(NOW()/DATE_ADD/ON DUPLICATE/SHOW TABLES)改 SQLite(独立 sessions.sqlite)
帖子/回复正文存在 threads.content / posts.content(MEDIUMTEXT)剥离 → extern/ .txt 文件(白皮书红线)
插件体系Plugin.php + 21 个 hook 散布在 index/init/控制器/视图保留,暂停迁移(P0~P6 插件不激活;测试期 Plugin::hook 调用临时注释/绕过,不删除)
浏览计数Thread::incrementView 实时 UPDATE改 APCu 内存计数 + 异步 flush(白皮书模块五)
搜索自研倒排索引 search_index 表 + bigram 分词已确认:保留自研倒排索引(平移至 SQLite)


八、目标架构总览(SplitDB v2.3 落地形态)

数据流:
  写入 = global_id 取号 → 季度分区 + ID%32 哈希 → 写 Bucket + 正文 extern → 随机入队 → 返回
  读取 = 列表/分页走 main_index → 详情走 bucket_path + extern → 搜索走 search_index(异步)

分层:
  data/meta/                → main_index.sqlite(帖子主索引) · business.sqlite(用户/版块/设置/
                              博客/私信/附件/标签/投票等非分片业务数据)
                              search_index.sqlite(自研倒排) · global_id.sqlite · sessions.sqlite
  data/meta/task_queue/     → queue_0~2.sqlite(三队列打散锁竞争)
  data/bucket/active/{季度}/{0..31}.sqlite  → topic / reply 真相源
  data/bucket/archive/      → 18 个月以上冷数据
  data/extern/{年}/{季度}/{桶}/{id}.txt     → 正文外置
  cli/                      → worker.php(队列) · rebuild_search.php · archive.php · migrate.php

已确认的设计决策(用户拍板):

  1. 搜索方案:保留自研倒排索引(Search.php + bigram 分词,表平移至 SQLite),不采用 FTS5
  2. 非分片数据:独立 business.sqlite(与 main_index 分离,避免索引写锁互相影响)

九、目录结构变动表

路径处理方式说明
config.php✏️ 重写去全部 MySQL 项;新增 DATA_PATH/BUCKET_SIZE/META_PATH/EXTERN_PATH
install.php✏️ 重写不再连 MySQL;调用 SplitDB\Schema::bootstrap() 建目录/库/表 + 建管理员
app/SplitDB/🆕 新增(核心引擎)DBFactory(LRU池8) · IDGenerator · ShardRouter(季度+哈希) · ExternStorage(原子写) · Queue+QueueConsumer(CAS) · Schema(bootstrap) · ViewCounter(APCu+降级)
app/Core/Database.php✏️ 重写SplitDB 门面(meta 库连接 + 事务),保留原 API;getSessionConnection → sessions.sqlite
app/Core/Model.php✏️ 改去反引号,SQLite 兼容,lastInsertId 改 lastInsertRowID
app/Models/Thread.php✏️ 重写分片写入/读取 + main_index + extern 正文
app/Models/Post.php✏️ 重写同上(回复表)
app/Models/User/Category/Setting/Blog/BlogCategory/BlogComment/Message/Tag✏️ 重写business.sqlite 查询,清理 MySQL 语法
app/init.php✏️ 改Session 改 SQLite;插件初始化与钩子临时绕过(注释,不删除)
app/Helpers/Plugin.php✅ 保留插件系统核心(P0~P6 暂停激活,不删除)
app/Controllers/Admin/PluginController.php✅ 保留插件后台管理(暂停迁移,不删除)
plugins/(整目录)✅ 保留插件目录结构保留(目录下插件已移除)
app/Helpers/*(Auth/Settings/Vote/Points/Tag/Search/Segmenter 等)✏️ 清理去 MySQL 语法;Search 平移至 SQLite(Plugin::hook 调用保留,测试期临时绕过)
app/Controllers/*(前台+后台)✏️ 改临时注释/绕过 Plugin::hook 调用(不删除)、数据流适配(约 12 处钩子点)
app/Views/*✏️ 改临时注释视图内钩子(nav_main_links/announcements_display/layout_head_end 等 7 处,不删除)
protected/database/schema.sql✏️ 重写SQLite DDL:meta 库表 + bucket 内 topic/reply 表
cli/🆕 新增worker.php · rebuild_search.php · archive.php · migrate.php · wal_reclaim 脚本
data/🆕 运行时生成白皮书四章完整目录树
update/🆕 新增同步所有改动文件 + CHANGELOG.md + 本计划文档(可运行骨架)
app/Core/Router/Autoloader/TemplateCompiler/Controller、assets/、主题✅ 保留仅 Controller 1 处钩子临时注释(不删除)


十、改造执行计划表(严格串行,每步验证通过才进下一步)

阶段任务交付物验证方式状态
P0 骨架与配置① 重写 install.php + 新增 Schema::bootstrap(建目录树/4 个 meta 库/队列库/首季度 32 桶)② 重写 config.php 生成模板空库可初始化php -l + 跑 install 生成目录树⏳ 待办
P1 核心引擎 ✅① DBFactory(LRU≤8、禁持久、统一 PRAGMA)② IDGenerator(global_id + BEGIN IMMEDIATE 事务 + 重试)③ ShardRouter(季度+ID%32)④ ExternStorage(tmp+rename 原子写 + APCu 降级)四个类 + 4 个冒烟脚本CLI 冒烟 50/50 通过✅ 已完成
P2 队列与消费① Queue(随机三队列分发)② QueueConsumer(CAS 抢占/超时恢复/重试上限)③ cli/worker.php异步队列可用压入 100 任务,worker 消费,验证 pending→processing→done/failed⏳ 待办
P3 数据库门面① 重写 Database.php(meta 库门面)② 改 Model.php ③ init.php Session 改 SQLite全站无 MySQL 连接grep 零残留 MySQL 关键字;页面可渲染⏳ 待办
P4 模型层① Thread/Post 分片重写(写桶+extern+main_index,读 main_index+桶)② User/Category/Setting 等 business.sqlite 化 ③ ViewCounter(无 APCu 静默降级为 UPDATE 实时计数,不崩站)Models 全 SQLiteCLI 发帖冒烟:分片文件/正文文件/索引行三处生成⏳ 待办
P5 控制器与视图① 发帖/回帖/列表/详情流程接 SplitDB ② 全站 // [SplitDB] 临时注释: 前缀注释 Plugin::hook(保留原始调用原文,不删除,确保测试期不触发插件逻辑)③ 后台适配(数据库页改 VACUUM/备份 SQLite)前台+后台可用phpstudy 手动发帖→列表→详情→回复→搜索全链路⏳ 待办
P6 索引与统计① 队列事件接 rebuild_search/stats ② cli/rebuild_search.php/archive.php ③ 凌晨统计最终一致性闭环删库重建索引,比对分片真相源⏳ 待办
P7 迁移与测试① cli/migrate.php(可选:读旧 MySQL → 按路由规则导入;正文 MEDIUMTEXT 必须成功剥离为 extern/ .txt,失败则报错回滚,杜绝「索引导入成功但正文丢失」)② 测试流程文档 ③ CHANGELOG.md 收尾迁移+验收按测试流程走通⏳ 待办


十一、update/ 同步与交付规范

  1. 同步规则:每改动一个文件,立即拷贝至 update/ 对应相对路径(含 config.php 模板、cli/、app/ 全量),保证 update/ 是可运行骨架
  2. CHANGELOG.md:每次模块改动后追加一条,格式 [P1] DBFactory.php — 新增 LRU 连接池(MAX_HANDLES=8)…,含文件清单与逻辑变更说明
  3. 编码安全:全部文件 UTF-8 without BOM、LF 行尾(逐文件校验确认)

十二、测试流程说明(交付后本地验收)

  1. phpstudy 起 Nginx + PHP 8.0.2(无需 MySQL)→ 访问 /install.php 初始化 → 确认 data/ 目录树生成
  2. 注册账号 → 发帖「测试标题」→ 验证:
  • data/bucket/active/{当前季度}/{id%32}.sqlite 出现 topic 行
  • data/extern/{年}/{季}/{桶}/{id}.txt 生成正文
  • main_index.sqlite 有索引行
  1. 首页/版块列表可见新帖(读 main_index);点开详情正文正常(读 extern);回复后 reply 落桶 + 回复计数异步更新
  2. 搜索关键词命中(search_index);php cli/worker.php 消费队列后索引一致

十三、进度状态

  • ✅ P1 核心引擎已完成(2026-08-13):DBFactory / IDGenerator / ShardRouter / ExternStorage + 4 个冒烟脚本,50/50 通过
  • 关键适配:PHP 8.0.2 的 SQLite 3.33.0 不支持 UPDATE…RETURNING(需 ≥3.35),IDGenerator 改用 BEGIN IMMEDIATE 写锁事务(等价原子性,兼容 7.4+)
  • PDO-SQLite 对 exec('BEGIN IMMEDIATE') 不置内部事务标志,事务控制全程用原始 SQL(exec('COMMIT')/exec('ROLLBACK')),避免 "There is no active transaction" 与残留事务问题
  • 全部文件 UTF-8 无 BOM + LF;update/ 已同步 8 个文件 + CHANGELOG.md
  • ⏳ 下一阶段:P2 任务队列(Queue + QueueConsumer + cli/worker.php),等待用户指令后开始
轻量级、高性能、零 MySQL 依赖的PHP社区系统。
| 瀏覽 5 次 | 回覆 0 次

全部回覆 (0)

暫無回覆

請 登入