# Ziin SQLite 到 MySQL 迁移说明 v1

适用场景：

- 当前 `ZIIN_DAO_DRIVER=sqlite`
- 已在试点或准商用阶段
- 需要切到更适合多实例、备份、运维和共享访问的 MySQL

## 一、迁移前提

需要先准备：

- 可用的 MySQL 数据库
- 正确的连接串 `ZIIN_MYSQL_URL`
- 当前 Ziin 服务目录下的 SQLite 数据文件

当前项目默认 SQLite 文件位置：

```bash
/root/ziin/data/ziin.sqlite
```

如果你不用单连接串，也可以用拆分变量：

```bash
export ZIIN_MYSQL_HOST=127.0.0.1
export ZIIN_MYSQL_PORT=3306
export ZIIN_MYSQL_USER=ziin_app
export ZIIN_MYSQL_PASSWORD='your-password'
export ZIIN_MYSQL_DATABASE=ziin
```

## 二、迁移脚本

仓库内置脚本：

```bash
scripts/migrate-sqlite-to-mysql.ts
```

## 三、先做 dry-run

先只读取 SQLite 当前数据量，不写入 MySQL：

```bash
cd /root/ziin
export ZIIN_MYSQL_URL='mysql://user:password@127.0.0.1:3306/ziin'
node --import tsx scripts/migrate-sqlite-to-mysql.ts --dry-run
```

输出会按表显示数量，例如：

```text
softwareDomains: 1
tenantDomains: 2
knowledgeAssets: 13
mediaFiles: 28
mediaParseTasks: 19
...
```

## 四、正式迁移

正式写入 MySQL：

```bash
cd /root/ziin
export ZIIN_MYSQL_URL='mysql://user:password@127.0.0.1:3306/ziin'
node --import tsx scripts/migrate-sqlite-to-mysql.ts
```

脚本会：

1. 自动初始化 MySQL 表结构
2. 从 SQLite 读取全部 JSON payload
3. 按表 upsert 到 MySQL
4. 输出每张表迁移数量

## 五、覆盖迁移

如果目标 MySQL 已有旧数据，想先清空再全量导入：

```bash
cd /root/ziin
export ZIIN_MYSQL_URL='mysql://user:password@127.0.0.1:3306/ziin'
node --import tsx scripts/migrate-sqlite-to-mysql.ts --truncate
```

注意：

- `--truncate` 会清空目标表
- 只建议在明确知道目标库可重建时使用

## 六、推荐的数据库准备方式

不建议长期直接用 MySQL `root` 跑应用。

更稳妥的方式是：

1. 单独创建数据库
2. 单独创建应用账号
3. 只给 Ziin 所需权限
4. 运行时用专用账号连接

示例：

```sql
CREATE DATABASE IF NOT EXISTS ziin
  DEFAULT CHARACTER SET utf8mb4
  DEFAULT COLLATE utf8mb4_unicode_ci;

CREATE USER IF NOT EXISTS 'ziin_app'@'127.0.0.1' IDENTIFIED BY 'replace-with-strong-password';
GRANT ALL PRIVILEGES ON ziin.* TO 'ziin_app'@'127.0.0.1';
FLUSH PRIVILEGES;
```

## 七、切换运行时

迁移完成后，把运行时 DAO 切换到 MySQL：

```bash
export ZIIN_DAO_DRIVER=mysql
export ZIIN_MYSQL_URL='mysql://ziin_app:password@127.0.0.1:3306/ziin'
```

如果你用 PM2：

```bash
pm2 startOrRestart /root/ziin/ecosystem.config.cjs --only ziin --update-env
pm2 startOrRestart /root/ziin/ecosystem.config.cjs --only ziin-media-worker --update-env
```

## 八、迁移后验证

建议至少验证这几项：

1. 健康接口

```bash
curl -sS https://ziin.shenliu.cc/api/ziin/health
```

确认：

- `config.driver = mysql`
- `config.mysqlUrlConfigured = true`
- 页面和接口可正常读取基础数据

2. 后台运行页

- 打开 `/admin/runtime`
- 确认 DAO Driver 已显示为 `mysql`

3. 多模态链路

- 上传一个文本、图片或音频文件
- 确认 `media_files`、`media_parse_tasks` 持续新增

## 九、建议顺序

更稳妥的方式不是直接切生产，而是：

1. 先 `dry-run`
2. 再迁移到新 MySQL
3. 用测试环境切到 `mysql`
4. 验证后台、上传、ASR/OCR、query
5. 最后再切正式流量

## 十、当前脚本边界

当前脚本是“全量 JSON payload 迁移”，优点是实现稳、对现有 DAO 改动小。

它的边界也很明确：

- 不做双写
- 不做增量同步
- 不做在线无感迁移

如果后面进入真正多节点商用，建议再补：

- 切换窗口方案
- 迁移校验报告
- 回滚预案
- 双写或只读冻结策略
