# 知习数据库与服务器部署方案（最终版）

> 定版日期：2026-05-18 | 状态：已拍板，按此执行

---

## 一、存储架构总览

知识库不需要 MongoDB。最终存储分成四类：

```text
MySQL  ：权威业务数据（用户、知识库、学习记录、额度、审计）
Qdrant ：向量索引（chunk embedding、语义检索）
COS    ：对象存储（原始文件、解析结果、备份快照）
Redis  ：队列 / 缓存 / 限流 / 锁
```

不引入的组件：

```text
MongoDB        → 不需要（没有文档型存储场景）
Elasticsearch  → 第一阶段不需要（Qdrant + rerank 够用）
ClickHouse     → 第一阶段不需要（OLAP 后续再说）
```

---

## 二、MySQL：唯一权威业务数据库

### 存放内容

```text
# 用户与认证
users
auth_accounts
refresh_tokens
memberships
membership_plans

# 知识库核心
files
knowledge_bases
knowledge_sources
document_imports
knowledge_chunks
import_candidates
knowledge_items

# 学习引擎
active_recall_answers
ai_analysis_results
focus_items
review_cards
learning_activities
chat_sessions
chat_messages

# 运营与审计
ai_usage_logs
quota_usage
admin_audit_logs
backup_jobs
feedback
notifications
```

### ID 类型（重要）

当前 Prisma schema 主键是 `BigInt @default(autoincrement())`，JavaScript/JSON 对 64 位整数有精度问题。

**现在数据库空，必须立即改：**

```prisma
id String @id @default(cuid())
```

推荐 `cuid/cuid2`：适合业务 ID，前端、后端、日志、JWT、API 都好处理。以后数据多了再改会很痛苦。

### 生产配置

文件：`/etc/mysql/conf.d/zhixi.cnf`

```ini
[mysqld]
# InnoDB
innodb_buffer_pool_size = 8G
innodb_buffer_pool_instances = 4
innodb_log_file_size = 1G
innodb_flush_log_at_trx_commit = 1
innodb_file_per_table = 1

# Connections
max_connections = 100
wait_timeout = 300
interactive_timeout = 300

# Charset
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

# Slow query
slow_query_log = 1
long_query_time = 1
log_queries_not_using_indexes = 0

# Binlog（无主从复制时可关）
skip-log-bin
```

### 32G 内存分配

```text
MySQL buffer pool   8G
Qdrant              8G–12G（动态）
Redis               1G 内
RAG Worker / 解析   4G–8G
系统 + Docker + 缓存 4G+
预留安全空间         4G+
```

后续调整规则：

```text
MySQL 慢查询多 + 内存充足 → buffer pool 调到 10G–12G
Qdrant / Worker 内存吃紧 → 不再加 MySQL 内存
Too many connections     → max_connections 调到 150

---

## 三、Qdrant：向量数据库

### 存放内容

```text
Collection：zhixi_chunks
├─ chunk embedding 向量（1024d，Cosine）
├─ userId / knowledgeBaseId / sourceId / chunkId（payload 索引）
├─ pageNumber / sectionTitle（元数据）
└─ deleted（bool，软删除标记）
```

### 负责的能力

```text
语义检索（ANN Top-50 → rerank Top-5~8）
知识库问答召回
相似知识点查找
学习内容推荐
```

### 不负责

```text
用户体系
会员额度
知识点管理
学习记录
订单支付
```

Qdrant 是 MySQL 的补充，不是替代。

### 安全策略（已拍板）

```text
第一阶段：不公网暴露 Qdrant
优先方案：不映射端口，只在 Docker internal network 用容器名访问
调试方案：仅绑定 127.0.0.1:6333
暂不启用 API Key，但预留 QDRANT_API_KEY 环境变量
未来跨服务器访问时 → 必须启用 API Key
```

### Docker Compose 推荐写法（不映射端口）

```yaml
services:
  qdrant:
    image: qdrant/qdrant:latest
    restart: unless-stopped
    volumes:
      - /data/qdrant:/qdrant/storage
    networks:
      - zhixi_internal
    # 不写 ports，公网访问不到

  backend:
    image: zhixi-backend:latest
    restart: unless-stopped
    environment:
      QDRANT_URL: http://qdrant:6333
    networks:
      - zhixi_internal
      - zhixi_public

networks:
  zhixi_internal:
    internal: true          # 不对外暴露
  zhixi_public:
```

### 如需宿主机调试（仅绑定 localhost）

```yaml
services:
  qdrant:
    ports:
      - "127.0.0.1:6333:6333"
      - "127.0.0.1:6334:6334"
```

### 不要这样写

```yaml
ports:
  - "6333:6333"            # 暴露公网！没有 API Key 时非常危险
```

---

## 四、COS：对象存储

### 存放内容

```text
zhixi-prod/
├─ users/{userId}/knowledge-bases/...  ← 原始文件 + 解析结果
├─ users/{userId}/profile/avatar/...   ← 头像
├─ users/{userId}/feedback/...         ← 反馈截图
├─ system/knowledge-bases/...          ← 系统内置知识库
├─ system/backups/qdrant/{date}/...    ← Qdrant 快照
├─ system/backups/mysql/{date}/...     ← MySQL 备份
├─ exports/users/{userId}/...          ← 用户导出
└─ public/app-assets/...               ← 公共资源
```

### 原则

```text
数据库是主关系，COS 是文件仓库
权限、归属、状态永远以数据库为准
处理文件时从 COS 临时拉取，处理完删除本地临时文件
```

---

## 五、Redis：队列与缓存

### 负责

```text
BullMQ 任务队列（文档导入、AI 分析、通知发送）
API 限流
分布式锁（防止重复导入、重复扣额度）
用户 session（备选，也可用 MySQL）
临时状态缓存
```

### 不负责

```text
长期业务数据
向量索引
文件存储
```

---

## 六、服务器分工（已拍板）

### 总原则

现在数据库基本空，**不要再把新表建到旧服务器上**。唯一权威 MySQL 放 8核32G。

### 8核32G CVM：正式生产主服务器

**部署清单：**

```text
/opt/zhixi/backend          ← NestJS 主后端
/opt/zhixi/rag-worker       ← Python RAG Worker
/opt/zhixi/deploy           ← docker-compose.yml + 脚本

/data/docker                ← Docker data-root
/data/mysql                 ← MySQL 数据文件
/data/redis                 ← Redis 数据文件
/data/qdrant                ← Qdrant 存储
/data/tmp/imports           ← 临时下载/解析工作区
/data/logs                  ← 统一日志
/data/worker-cache          ← llama-index 缓存
/data/backups/mysql         ← MySQL 每日备份
/data/backups/qdrant        ← Qdrant 每日快照
```

**运行服务：**

```text
Nginx（可选）
NestJS API（Node）
MySQL 8.0
Redis 7
Qdrant（Docker）
RAG Worker（Python）
AI Gateway Worker（Node）
Backup Worker（Cron）
Cron Jobs（定时清理、stale job recovery）
```

**负责的业务：**

```text
全部用户数据、认证、会员
全部知识库数据（表 + 向量）
全部学习引擎（主动回忆、AI 诊断、复习）
全部 AI 调用与成本记录
全部后台管理与审计
```

### 4核4G 轻量云：工具服务器 / 辅助服务器

**定位：** 继续当工具服务器，不承载生产核心数据。

**已运行服务（保留）：**

```text
Web 产品页 / landing page
Gitea
```

**适合新增的服务：**

```text
n8n                         ← 营销自动化、流程编排、Webhook
轻量 agent runner            ← CI/CD 辅助、定时调研、通知
Uptime Kuma                  ← 服务监控
跳板机                       ← SSH 入口
测试 / staging 环境           ← 预发布验证
Gitea Webhook 自动化          ← CI/CD 触发
营销自动化脚本                ← 定时任务
轻量后台工具                  ← 不操作生产库的管理工具
```

**不建议放 4核4G 的服务：**

```text
正式 MySQL / Redis / Qdrant  ← 生产核心数据
RAG Worker / 文档解析         ← 重型 CPU/内存任务
Dify 生产环境                 ← 最低 4G 但跑不稳（后面细说）
本地大模型 / 多模态模型        ← GPU/内存不够
重型爬虫 / 大量自动化任务      ← 吃满 CPU
```

---

## 七、4核4G 上各服务决策

### 1. n8n：可以放

n8n 适合放 4核4G，用来做：

```text
营销自动化
内容生产流程编排
Gitea Webhook 触发
定时任务
数据同步
通知提醒
AI API 调用编排
运营脚本
```

部署方式：

```text
Docker Compose
nginx/caddy 反代 → https://n8n.yourdomain.com
```

安全要求：

```text
强密码
HTTPS
禁止公开注册
后台路径不裸奔
定期备份 n8n 数据目录
```

**核心规则：n8n 不直接操作生产数据库。**

正确方式：

```text
n8n / agent
→ 调用 8核32G 的受控内部 API
→ 8核32G 做权限、额度、日志、落库
```

不要让 n8n 或 agent 变成"野生后台"。

### 2. Dify：第一阶段不部署

Dify 最低要求 CPU ≥ 2 Core、RAM ≥ 4 GiB（官方推荐 8 GiB 更稳）。4核4G 已有 Web 产品页 + Gitea + n8n + agent，再放 Dify 容易"能启动但卡、能用但不稳、一跑 workflow 就吃满"。

而且 Dify 和你自己的 AI Gateway / RAG Worker 有功能重叠，知习核心知识库才是优先事项。

**决策：**

```text
第一阶段不部署 Dify。
后续如需实验，放 8核32G sandbox profile（不连生产库）。
```

```bash
# 以后如需实验：
docker compose --profile dify up -d   # 用完关掉
```

### 3. 轻量 agent：可以放

适合放 4核4G 的 agent 类型：

```text
营销内容生成 agent
定时调研 agent
Gitea issue 分析 agent
自动发布提醒 agent
数据采集辅助 agent
n8n workflow agent
小型 webhook agent
```

规则和 n8n 一样：**agent 可以调用知习 API，但不可直接连生产 MySQL、操作 Qdrant、拿 COS 主密钥。**

---

## 八、最终部署架构图

```text
                     iOS / Web
                        ↓
        ┌───────────────┴───────────────┐
        │                               │
┌───────┴──────────┐           ┌───────┴──────────┐
│  8核32G CVM      │           │  4核4G 轻量云      │
│  (生产核心服务器)  │           │  (工具/辅助服务器)  │
│                  │           │                   │
│ ┌──────────────┐ │           │ ┌───────────────┐ │
│ │ gitea-runner │ │ CI/CD     │ │ gitea-runner  │ │
│ │   -prod      │ │           │ │   -web        │ │
│ └──────────────┘ │           │ └───────────────┘ │
│ ┌──────────────┐ │           │ ┌───────────────┐ │
│ │  NestJS API  │ │           │ │  Web 产品页    │ │
│ └──────┬───────┘ │           │ │  (landing)     │ │
│        │         │           │ └───────────────┘ │
│ ┌──────┴───────┐ │           │ ┌───────────────┐ │
│ │    MySQL     │ │ 权威业务库 │ │    Gitea      │ │
│ └──────────────┘ │           │ │   (主服务)     │ │
│ ┌──────────────┐ │           │ └───────────────┘ │
│ │    Redis     │ │ 队列/缓存  │ ┌───────────────┐ │
│ └──────────────┘ │           │ │     n8n       │ │
│ ┌──────────────┐ │           │ └───────────────┘ │
│ │    Qdrant    │ │ 向量索引   │ ┌───────────────┐ │
│ └──────────────┘ │           │ │  agent runner │ │
│ ┌──────────────┐ │           │ └───────────────┘ │
│ │  RAG Worker  │ │ 解析/切片  │ ┌───────────────┐ │
│ └──────────────┘ │           │ │ Uptime Kuma   │ │
│ ┌──────────────┐ │           │ └───────────────┘ │
│ │  AI Gateway  │ │ AI 调度   │                   │
│ └──────────────┘ │           │ (不承载正式       │
│ ┌──────────────┐ │           │  业务数据)         │
│ │ Backup+Cron  │ │ 备份/清理  └───────────────────┘
│ └──────────────┘ │
│                  │
│    /data (70G)   │
└────────┬─────────┘
         │
 ┌───────┴───────┐
 │  腾讯云 COS    │
 │               │
 │  原始文件      │
 │  parsed.md    │
 │  OCR/Vision   │
 │  备份快照      │
 │  用户头像      │
 │  导出文件      │
 └───────────────┘
```

---

## 九、CI/CD 部署流水线

### 1. 总原则

```text
Gitea 本体只放 4核4G，Runner 两台都装
服务跑在哪台服务器，Runner 就装在哪台服务器
每台 Runner 本地构建、本地部署，不跨服务器传输构建产物
```

不要搞成"4核 Runner 构建后端 → 传到 8核部署"，多一层 SSH + 传输 + 1Mbps 带宽瓶颈。

### 2. Runner 分工

**4核4G：gitea-runner-web**

```text
标签：web, tools, staging, docker
负责：
  Web 产品页部署
  Gitea Webhook 自动化
  n8n / 工具服务部署
  轻量 agent 更新
```

**8核32G：gitea-runner-prod**

```text
标签：prod, backend, rag, docker
负责：
  NestJS 后端部署
  Prisma migration
  MySQL / Redis / Qdrant docker compose 更新
  RAG Worker 部署
  AI Gateway 部署
  备份脚本部署
```

### 3. Runner 安装

两台服务器都创建 `deploy` 用户（不要用 root 跑 runner）：

```bash
sudo useradd -m -s /bin/bash deploy
sudo usermod -aG docker deploy
```

8核上注册 runner 到 4核 Gitea：

```bash
# 在 8核32G 上执行
./gitea-actions-runner register \
  --name zhixi-prod-runner \
  --labels prod,backend,rag,docker \
  --instance https://gitea.yourdomain.com \
  --token <从 Gitea Admin 获取>
```

### 4. Workflow 示例

**后端部署（跑在 8核 runner）：**

```yaml
# .gitea/workflows/deploy-backend.yml
name: deploy-backend

on:
  push:
    branches: [main]
    paths:
      - "backend/**"
      - "rag-worker/**"
      - "deploy/**"

jobs:
  deploy:
    runs-on: [prod, backend, docker]
    steps:
      - uses: actions/checkout@v4
      - name: Build & Deploy
        run: |
          cd backend
          pnpm install --frozen-lockfile
          pnpm build
          npx prisma migrate deploy
          docker compose -f ../deploy/docker-compose.prod.yml up -d --build
```

**Web 部署（跑在 4核 runner）：**

```yaml
# .gitea/workflows/deploy-web.yml
name: deploy-web

on:
  push:
    branches: [main]
    paths:
      - "web/**"

jobs:
  deploy:
    runs-on: [web, docker]
    steps:
      - uses: actions/checkout@v4
      - name: Build & Deploy
        run: |
          cd web
          pnpm install --frozen-lockfile
          pnpm build
          docker compose -f docker-compose.web.yml up -d --build
```

### 5. 密钥管理

生产密钥不写入仓库，放在 8核服务器本地或 Gitea Secrets：

```text
/opt/zhixi/env/.env.production

必需密钥：
  DATABASE_URL
  REDIS_URL
  QDRANT_URL
  COS_SECRET_ID / COS_SECRET_KEY
  DEEPSEEK_API_KEY
  SILICONFLOW_API_KEY
  BAIDU_OCR_KEY
  JWT_SECRET
  RAG_WORKER_SECRET（内部 API 认证）
```

### 6. Gitea 挂了不影响生产

```text
Gitea 只影响代码仓库和部署流水线
8核上的后端、MySQL、Redis、Qdrant 继续运行
最多暂时不能自动部署新版本
```

---

## 十、迁移执行计划

因为现在数据库基本空，迁移很简单，按此顺序推进：

### 第一步：8核32G 基础环境

```text
1. 挂载 /data 数据盘
2. 安装 Docker / Docker Compose
3. 配置 Docker data-root → /data/docker
4. 创建所有 /data 子目录
5. 创建 deploy 用户（用于 Runner 和部署）
```

### 第二步：部署核心服务

```text
6. 部署 MySQL → /data/mysql，创建数据库 zhixi_prod
7. 部署 Redis → /data/redis
8. 部署 Qdrant → /data/qdrant，创建 collection zhixi_chunks
9. 配置数据库连接：DATABASE_URL / REDIS_URL / QDRANT_URL
```

### 第三步：数据库迁移

```text
9. 修改 Prisma schema：BigInt → String(cuid)
10. 初始化空库 migration
11. 4核4G mysqldump → 导入 8核32G（仅登录相关 2~3 张表）
12. 配置备份脚本（MySQL + Qdrant + 同步 COS）
```

### 第四步：CI/CD 搭建

```text
14. 8核32G 安装 gitea-runner-prod（标签 prod, backend, rag, docker）
15. 4核4G 安装 gitea-runner-web（标签 web, tools, docker）
16. 配置 Gitea Secrets（密钥不入仓库）
17. 编写 deploy-backend.yml / deploy-web.yml
18. 测试 push → 自动构建 → 自动部署流水线
```

### 第五步：DNS 与后端切换

域名规划：

```text
zhixi.app / api.zhixi.app     → 8核32G（NestJS 后端 API）
www.zhixi.app / zhixi.app     → 4核4G（Web 产品页）
n8n.zhixi.app                 → 4核4G（n8n）
gitea.zhixi.app               → 4核4G（Gitea）
```

```text
19. 确认 8核32G 上 NestJS 正常运行（先用 IP:3000 测试）
20. DNS 主域名（api.zhixi.app / 主站）从 4核4G 切换到 8核32G
21. Caddy/Nginx 配置 SSL 证书（Let's Encrypt 自动申请）
22. 修改 DATABASE_URL 指向新 MySQL
23. 测试 iOS 登录功能（通过域名 + HTTPS）
24. 4核4G 旧后端保留 7~14 天作为回滚备用
25. n8n / Gitea 的子域名保持指向 4核4G（不动）
```

> **当前状态：DNS 尚在 4核4G。切换完成后告知，更新本文档状态。**

### 第六步：知识库开发

```text
23. 在 8核32G 上按知识库设计文档建表
24. 实现文件上传 → COS
25. 部署 RAG Worker
26. 打通索引流程 → 学习流程
```

---

## 十一、最终拍板清单

```text
# 存储架构
1. 唯一权威 MySQL 放 8核32G，4核4G 不再建新表
2. ID 类型立即从 BigInt 改为 String(cuid)，趁库空
3. Redis 放 8核32G（队列 / 缓存 / 限流 / 锁）
4. Qdrant 放 8核32G（向量索引）
5. COS 继续作为文件 + 备份存储
6. 不引入 MongoDB / Elasticsearch / ClickHouse

# 8核32G：生产核心服务器
7. 全量承载 NestJS + MySQL + Redis + Qdrant + RAG Worker + AI Gateway
8. 负责全部用户数据、知识库、学习引擎、成本记录、后台审计
9. 安装 gitea-runner-prod（负责后端/知识库/RAG 部署）

# 4核4G：工具服务器 / 辅助服务器
10. 保留 Gitea 主服务 + Web 产品页
11. 安装 gitea-runner-web（负责 Web/工具/n8n 部署）
12. 可以加 n8n（营销自动化、Webhook、流程编排）
13. 可以加轻量 agent runner（不直接操作生产库）
14. 可以加 Uptime Kuma 监控
15. Dify 第一阶段不部署（后续如需实验放 8核32G sandbox）
16. 不承载正式 MySQL / Qdrant / Redis / RAG Worker

# CI/CD
17. Gitea 本体只放 4核4G，Runner 两台都装
18. 服务跑在哪台服务器，Runner 就装在哪台服务器
19. 生产密钥放 Gitea Secrets 或服务器本地 .env，不写入仓库
20. Runner 使用 deploy 用户，不用 root

# 安全边界
21. n8n / agent 调用知习受控 API，不直连生产库
22. 迁移完成后 4核4G 旧后端保留 7~14 天作为回滚备用
```

后续不会再有一次大的数据库迁移。

---

## 十二、仍存缺陷与待决策项

以下是当前方案中尚未覆盖或需要你拍板的点，按风险等级排序。

### 🔴 高风险（上线前必须定）

| # | 问题 | 现状 | 建议 |
|---|------|------|------|
| 1 | **3Mbps 公网带宽** | 8核32G 3Mbps | 日常增量文件（单文件 ≤20MB）影响可控。大文件下载 COS + 备份上传走后台队列异步处理，不阻塞 API。首次部署大量依赖包安装可能稍慢，耐心等待即可。 |
| 2 | **Qdrant 无内置认证** | 已定 | 第一阶段不公网暴露。优先不映射端口（Docker internal network），需调试时仅绑定 `127.0.0.1:6333`。暂不启用 API Key，预留 `QDRANT_API_KEY` 配置。跨服务器访问时必须启用。 |
| 3 | **HTTPS 证书** | 已定：免费证书（Let's Encrypt） | 域名解析当前指向 4核4G，待切换到 8核32G。切换后：域名 → 8核32G 公网 IP → Caddy/Nginx 反代 → 本地 NestJS:3000。Caddy 自动申请续期。 |
| 4 | **MySQL 配置调优** | 已定 | `innodb_buffer_pool_size=8G`，`max_connections=100`，完整配置见下方 MySQL 配置小节。后续观察慢查询/内存压力再调。 |

### 🟡 中风险（迁移前应定）

| # | 问题 | 现状 | 建议 |
|---|------|------|------|
| 5 | **Redis 持久化** | 未配置 | 推荐 AOF everysec + RDB 双开。Redis 主要做队列（BullMQ），任务丢失影响可控但要尽量保留。 |
| 6 | **Docker 容器网络规划** | 未定义 | 建议一个 `zhixi` 自定义 bridge 网络，MySQL/Redis/Qdrant 只监听 localhost 或容器内网，不绑定 0.0.0.0。 |
| 7 | **日志轮转与保留** | /data/logs 目录已规划但无策略 | 建议 logrotate：每天轮转，保留 30 天，压缩。Worker 日志量可能较大，需要单独评估。 |
| 8 | **健康检查端点** | 未定义 | NestJS 需 `/health` 端点（返回 DB/Redis/Qdrant 连通性）。Uptime Kuma 可用此端点监控。 |
| 9 | **两服务器间通信** | 内网还是公网？| 看腾讯云是否支持同地域内网互通（北京）。如果可以，Gitea → Runner 通信走内网更快。如果不能，Runner 通过公网拉取 Gitea 任务。 |
| 10 | **COS 带宽成本** | 备份上传/下载走公网 | COS 同地域内网访问免费。确认 8核32G 和 COS 都在"北京"地域，使用内网 Endpoint（`cos.ap-beijing.myqcloud.com` 的内网域名）。 |

### 🟢 低风险（后续迭代时再定）

| # | 问题 | 现状 | 建议 |
|---|------|------|------|
| 11 | **API 版本管理** | 未定义 | 建议 `/api/v1/...` 前缀，为后续 Breaking Change 留空间。 |
| 12 | **灰度/蓝绿部署** | 未涉及 | 第一阶段不需要。先单实例跑稳，后续再考虑 `docker compose` 滚动更新。 |
| 13 | **数据库读写分离** | 未涉及 | 第一阶段不需要。MySQL 单实例完全够。 |
| 14 | **异地备份** | COS 在北京 | COS 本身支持跨地域复制。如果预算允许，可以复制到另一个地域的 bucket。目前本地 + COS 同地域已较安全。 |
| 15 | **4核4G 最终去留** | 迁移后保留 7~14 天 | 后续如果 4核4G 作为长期工具服务器，建议保留。如果成本敏感且 8核32G 完全够用，可以到期前评估是否下线。 |
| 16 | **Docker Compose 文件结构** | 未定义 | 建议 `deploy/docker-compose.prod.yml` 统一管理 8核32G 上的所有服务，用一个 compose 文件管理容器间的启动顺序和网络。 |

---

## 十三、文档关联索引

本文档应与其他设计文档配合使用：

```text
知识库设计.MD              → 数据模型、API 设计、RAG 流程、额度系统
服务器与数据库部署方案.MD    → 本文档：服务器分工、存储架构、CI/CD
（后续）Prisma Schema     → 具体表结构实现
（后续）docker-compose.yml → 容器编排配置
```

