# Linux Docker 版:业务库切换 MySQL 发版说明 适用场景:Linux 服务器上使用 `offline-backend-20260512/start_backend.sh` 启动 Docker 后端。 原启动脚本保持不变: ```bash bash start_backend.sh ``` 单独新增切换脚本: ```bash bash switch_database_to_mysql.sh ``` ## 一、切换后的数据布局 迁移到 MySQL 的业务表: ```text users threads_meta runs run_events feedback agents skills scheduled_tasks scheduled_task_runs scheduled_task_subscriptions scheduled_task_delivery_profiles scheduler_threads llm_call_metrics notifications recommended_questions tags tag_assignments thread_shares ``` 继续留在 SQLite: ```text checkpoints writes ``` SQLite 路径: ```text 宿主机: offline-backend-20260512/data/data/deerflow.db 容器内: /app/backend/.deer-flow/data/deerflow.db ``` ## 二、前置条件 MySQL 建议提前建库: ```sql CREATE DATABASE deerflow CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ``` 本次 Linux 内网库连接信息按以下方式配置: ```bash MYSQL_DATABASE_URL='mysql+asyncmy://root:1qaz%40WSX@172.16.4.xxx:3306/deerflow?charset=utf8mb4' ``` 注意:密码里的 `@` 在 URL 中必须写成 `%40`,所以 `1qaz@WSX` 要写成 `1qaz%40WSX`。 离线包 `wheelhouse` 中需要有: ```text asyncmy-0.2.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl ``` 当前包已经放入该 wheel。 如果使用内网 pip 私服,在 `.env` 中配置: ```env PIP_INDEX_URL=http://your-pip-mirror/simple PIP_TRUSTED_HOST=your-pip-mirror UV_INDEX_URL=http://your-pip-mirror/simple UV_TRUSTED_HOST=your-pip-mirror ``` 这些变量会传给后端容器和 AIO 沙箱容器。 ## 三、发版步骤 进入部署目录: ```bash cd offline-backend-20260512 ``` 先启动一次后端,让 SQLite schema 自动升级到当前代码版本: ```bash bash start_backend.sh ``` 确认服务正常后停止: ```bash bash stop_backend.sh ``` 执行一键切换: ```bash MYSQL_DATABASE_URL='mysql+asyncmy://root:1qaz%40WSX@172.16.4.xxx:3306/deerflow?charset=utf8mb4' \ bash switch_database_to_mysql.sh ``` 如果数据库 `deerflow` 已经由 DBA 提前建好,或者当前账号不能访问 MySQL 系统库 `mysql`,可以跳过自动建库: ```bash CREATE_DATABASE=0 \ MYSQL_DATABASE_URL='mysql+asyncmy://root:1qaz%40WSX@172.16.4.xxx:3306/deerflow?charset=utf8mb4' \ bash switch_database_to_mysql.sh ``` 如果目标 MySQL 表已有旧数据,并确认要清空后重迁: ```bash TRUNCATE_TARGET=1 \ MYSQL_DATABASE_URL='mysql+asyncmy://root:1qaz%40WSX@172.16.4.xxx:3306/deerflow?charset=utf8mb4' \ bash switch_database_to_mysql.sh ``` 切换完成后,继续用原启动脚本: ```bash bash start_backend.sh ``` ## 四、脚本会自动做什么 `switch_database_to_mysql.sh` 会自动: 1. 检查 Docker 是否可用。 2. 检查后端容器是否已停止。 3. 备份: ```text data/backups/mysql-switch-YYYYMMDD_HHMMSS/config.yaml.bak data/backups/mysql-switch-YYYYMMDD_HHMMSS/env.bak data/backups/mysql-switch-YYYYMMDD_HHMMSS/deerflow.db.bak ``` 4. 启动一次临时迁移容器。 5. 从 `/app/wheelhouse` 安装 `asyncmy` 到持久目录: ```text 容器内: /app/backend/.deer-flow/python-vendor 宿主机: offline-backend-20260512/data/python-vendor ``` 6. 创建/升级 MySQL schema。 7. 默认清理失败重跑留下的空表: ```text DROP_EMPTY_TARGET_SCHEMA=1 ``` 该清理只会删除已知业务表中“全部为空”的半成品表;如果任意表已有数据,脚本不会删除,会继续交给迁移校验保护。 8. 从 SQLite 迁移业务表到 MySQL。 9. 校验每张业务表行数。 10. 修改 `config.yaml`: ```yaml database: backend: mysql mysql_url: $MYSQL_DATABASE_URL pool_size: 10 echo_sql: false checkpointer: type: sqlite connection_string: /app/backend/.deer-flow/data/deerflow.db ``` 11. 将 `MYSQL_DATABASE_URL` 写入 `.env`。 ## 五、验证 健康检查: ```bash curl http://:8001/health ``` 功能验证: ```text 登录/退出 历史会话列表 打开旧会话 新建对话 定时任务 分享链接 后端日志无 MySQL driver 或连接错误 ``` ## 六、回滚 停止后端: ```bash bash stop_backend.sh ``` 恢复备份: ```bash cp data/backups/mysql-switch-YYYYMMDD_HHMMSS/config.yaml.bak config.yaml cp data/backups/mysql-switch-YYYYMMDD_HHMMSS/env.bak .env cp data/backups/mysql-switch-YYYYMMDD_HHMMSS/deerflow.db.bak data/data/deerflow.db ``` 重新启动: ```bash bash start_backend.sh ``` ## 七、注意事项 - `MYSQL_DATABASE_URL` 不要写 `127.0.0.1` 指向宿主机 MySQL。容器内的 `127.0.0.1` 是容器自己。 - 如果 MySQL 在宿主机,使用宿主机内网 IP 或 Docker 可访问主机名。 - 当前 Linux 内网 MySQL 示例使用 `172.16.4.xxx:3306`。 - 密码含 `@`、`#`、`?`、`/` 等 URL 特殊字符时必须 URL 编码;本次 `1qaz@WSX` 写成 `1qaz%40WSX`。 - 已同步 Windows 实测问题修复: ```text 老 MySQL 767 bytes 索引长度限制:已收短被索引/唯一约束覆盖的字符串列。 老 MySQL 不支持 JSON 类型:MySQL 下 JSON 字段自动使用 TEXT 存储,应用层仍读写 dict/list。 失败重跑的半成品空表:switch_database_to_mysql.sh 默认传入 --drop-empty-target-schema 自动清理。 ``` - `data/data/deerflow.db` 不能删除,因为 `checkpoints/writes` 仍在里面。 - 后续归档文件建议放: ```text 容器内: /app/backend/.deer-flow/archives/conversations 宿主机: offline-backend-20260512/data/archives/conversations ``` 该路径已在现有 Docker 持久卷内,不需要改启动脚本。