# 知习数据库与服务器部署方案(最终版) > 定版日期:2026-05-18 | 状态:已拍板,按此执行 | 最后检查:2026-05-20 14:00 > > 完成度标记:✅ 已完成 | 🔶 部分完成 | ⏳ 待完成 | ❌ 未开始 --- ## 一、存储架构总览 ✅ 已完成 知识库不需要 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 类型(重要)✅ 已改为 String(cuid) 当前 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:对象存储 ⚠️ 凭据已配置,Bucket 待验证 ### 存放内容 ```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:队列与缓存 ✅ 已完成(BullMQ 待接) ### 负责 ```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 ``` --- ## 6.5、两服务器内网互联 ✅ 已验证 | 项目 | 值 | |------|-----| | 8核32G 内网 IP | 172.21.0.4 | | 4核4G 内网 IP | 10.2.0.7 | | 延迟 | ~1.9ms(双向 ping) | | 通信原则 | **所有服务器间通信均走内网,不走公网** | ### 已验证的通信路径 | 路径 | 内网地址 | 验证结果 | |------|---------|---------| | 8核32G → 4核4G Gitea | `http://10.2.0.7:3000` | ✅ HTTP 200, 6.5ms | | 4核4G → 8核32G API | `http://172.21.0.4:3000` | ✅ 正常响应 | | 8核32G Runner 注册 | `http://10.2.0.7:3000` | ✅ 已切换,restart 正常 | ### 对内网通信的服务 | 服务 | 原地址 | 切换后 | |------|--------|--------| | zhixi-prod-runner → Gitea | `81.70.187.179`(公网) | `10.2.0.7`(内网) | | 4核4G CI/CD → 8核32G API | `api.longde.cloud` | 可切 `172.21.0.4`(待优化) | | COS 上传/下载 | `cos.ap-beijing.myqcloud.com`(内网) | 同区 VPC 内网,免流量费 | --- ## 七、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 部署流水线 ✅ api-server + web-projects 均已配置 ### 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 安装 ✅ 已完成 - ✅ 8核32G: `gitea-runner` systemd 自启 - ✅ 4核4G: `gitea-runner` systemd 自启(单一 runner 同时处理 api-server 和 web-projects) ```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 实际部署 ✅ 已落地 **后端部署(4核4G runner 执行):** - 实际文件:`/home/ubuntu/api-server/.gitea/workflows/deploy.yml` - 流程:git pull → Docker build → 处理迁移卡死 → 启动 zhixi-api 容器 → 健康检查 **Web 部署(4核4G runner 执行):** - 实际文件:`/tmp/web-projects/.gitea/workflows/deploy.yml` - 流程:git clone/pull → Astro build (Docker node:22) → Nginx 配置 → 部署到 /var/www/longde.cloud → 确保后端运行 → Nginx reload - 最后部署:2026-05-20 13:38 ✅ ### 5. 密钥管理 ✅ 已配置 生产密钥已放在服务器本地: ```text 8核32G: /opt/zhixi/env/.env.production ✅ 全部必需密钥已配置 4核4G: /etc/zhixi/.env.production ✅(含 COS/DEEPSEEK/SILICONFLOW/JWT) rag-worker: /opt/zhixi/backend/rag-worker/.env ✅ 已配置: DATABASE_URL ✅ REDIS_HOST/REDIS_PORT/REDIS_PASSWORD ✅ QDRANT_URL ✅ COS_SECRET_ID / COS_SECRET_KEY ✅ DEEPSEEK_API_KEY ✅ SILICONFLOW_API_KEY ✅ JWT_SECRET ✅ RAG_WORKER_SECRET ✅ BAIDU_OCR_KEY ❌ 待开通 ``` ### 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 ✅ (3 个 migration 执行) 11. 4核4G mysqldump → 导入 8核32G ✅ 12. 配置备份脚本(MySQL + Qdrant + 同步 COS)⏳ 待配置 ``` ### 第四步:CI/CD 搭建 ✅ 已完成 ```text 14. 8核32G 安装 gitea-runner-prod ✅ 15. 4核4G 安装 gitea-runner-web ✅ (单一 runner 覆盖两端) 16. 配置 Gitea Secrets ✅ 17. 编写 deploy-backend.yml / deploy-web.yml ✅ 18. 测试 push → 自动构建 → 自动部署流水线 ✅ (web 今日 13:38 部署成功) ``` ### 第五步:DNS 与后端切换 ✅ 已完成 ```text 19. 确认 8核32G 上 NestJS 正常运行 ✅ 20. DNS api.longde.cloud 切换到 8核32G ✅ 21. Nginx 配置 SSL 证书 ✅ (Let's Encrypt) 22. 修改 DATABASE_URL 指向新 MySQL ✅ 23. 测试 iOS 登录功能(通过域名 + HTTPS)✅ ``` > **当前状态:DNS 已切换到 8核32G。✅** ### 第六步:知识库开发 🔶 进行中 ```text 23. 在 8核32G 上按知识库设计文档建表 ✅ (33 张表) 24. 实现文件上传 → COS ⏳ (COS 凭据已配,API 代码已就) 25. 部署 RAG Worker 🔶 (代码已部署,systemd 待创建) 26. 打通索引流程 → 学习流程 ⏳ (待 RAG Worker 跑通) ``` --- ## 十一、最终拍板清单(执行状态) ```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(单一 runner 覆盖 web + api-server 两端) 12. ⏳ 可以加 n8n(营销自动化、Webhook、流程编排) 13. ⏳ 可以加轻量 agent runner(不直接操作生产库) 14. ⏳ 可以加 Uptime Kuma 监控 15. ✅ Dify 第一阶段不部署 16. ✅ 不承载正式 MySQL / Qdrant / Redis / RAG Worker # CI/CD 17. ✅ Gitea 本体只放 4核4G,Runner 两台都装 18. ✅ 服务跑在哪台服务器,Runner 就装在哪台服务器 19. ✅ 生产密钥放服务器本地 .env,不写入仓库 20. ✅ Runner 使用 deploy 用户/ubuntu,不用 root # 安全边界 21. ⏳ n8n / agent 调用知习受控 API,不直连生产库(n8n 未部署) 22. ✅ 迁移完成后 4核4G 旧后端保留作为备用 ``` 后续不会再有一次大的数据库迁移。 --- ## 十二、仍存缺陷与待决策项(状态更新) 以下是当前方案中尚未覆盖或需要你拍板的点,按风险等级排序。 ### 🔴 高风险(上线前必须定) | # | 问题 | 现状 | 建议 | |---|------|------|------| | 1 | **3Mbps 公网带宽** | ✅ 已处理 | 日常增量文件(单文件 ≤20MB)影响可控。大文件下载 COS + 备份上传走后台队列异步处理,不阻塞 API。 | | 2 | **Qdrant 无内置认证** | ✅ 已定 | 仅绑定 `127.0.0.1:6333`,不公网暴露。 | | 3 | **HTTPS 证书** | ✅ 已配置 | Let's Encrypt 已生效,api.longde.cloud + longde.cloud + git.longde.cloud | | 4 | **MySQL 配置调优** | ✅ 已应用 | `innodb_buffer_pool_size=8G`,`max_connections=100` | ### 🟡 中风险(迁移前应定) | # | 问题 | 现状 | 建议 | |---|------|------|------| | 5 | **Redis 持久化** | ✅ 已配置 | AOF everysec + RDB 双开 | | 6 | **Docker 容器网络规划** | ✅ 已配置 | `zhixi-net` bridge 网络,MySQL/Redis 绑定 localhost | | 7 | **日志轮转与保留** | ✅ /data/logs 目录已建 | logrotate 待确认 | | 8 | **健康检查端点** | ✅ 已实现 | `/health` 端点返回 `{"status":"ok"}`,被 CI/CD 使用 | | 9 | **两服务器间通信** | ✅ 内网直连 | 双向 ~1.9ms,Runner/API 互调均走内网 | | 10 | **COS 带宽成本** | ✅ Bucket 存在 | `zhixi-1259685406` 已确认 | ### 🟢 低风险(后续迭代时再定) | # | 问题 | 现状 | |---|------|------| | 11 | **API 版本管理** | ⏳ `/api/v1/` 未加,当前无版本前缀 | | 12 | **灰度/蓝绿部署** | ⏳ 后续 | | 13 | **数据库读写分离** | ⏳ 后续 | | 14 | **异地备份** | ⏳ COS 备份待配置 | | 15 | **4核4G 最终去留** | ✅ 保留作为工具服务器 | | 16 | **Docker Compose 文件结构** | 🔶 当前用独立 docker run,未统一 compose | --- ## 十三、文档关联索引 本文档应与其他设计文档配合使用: ```text 知识库设计.MD → 数据模型、API 设计、RAG 流程、额度系统 服务器与数据库部署方案.MD → 本文档:服务器分工、存储架构、CI/CD (后续)Prisma Schema → 具体表结构实现 (后续)docker-compose.yml → 容器编排配置 ```