Files
AIclinicalresearch/docs/05-部署文档/PostgreSQL部署策略-摸底报告.md
HaHafeng 19f9c5ea93 docs(deployment): Fix 8 critical deployment issues and enhance documentation
Summary of fixes:
- Fix service discovery address (change .sae domain to internal IP)
- Unify timezone configuration (Asia/Shanghai for all services)
- Enhance ECS security group configuration (Redis/Weaviate port binding)
- Add image pull strategy best practices
- Add Python service memory management guidelines
- Update Dify API Key deployment strategy (avoid deadlock)
- Add SSH tunnel for RDS database access
- Add NAT gateway cost optimization explanation

Modified files (7 docs):
- 00-部署架构总览.md (enhanced with 7 sections)
- 03-Dify-ECS部署完全指南.md (security hardening)
- 04-Python微服务-SAE容器部署指南.md (timezone + service discovery)
- 05-Node.js后端-SAE容器部署指南.md (timezone configuration)
- PostgreSQL部署策略-摸底报告.md (timezone best practice)
- 07-关键配置补充说明.md (3 new sections)
- 08-部署检查清单.md (service address fix)

New files:
- 文档修正报告-20251214.md (comprehensive fix report)
- Review documents from technical team

Impact:
- Fixed 3 P0/P1 critical issues (100% connection failure risk)
- Fixed 3 P2 important issues (stability and maintainability)
- Added 2 P3 best practices (developer convenience)

Status: All deployment documents reviewed and corrected, ready for production deployment
2025-12-14 13:25:28 +08:00

41 KiB
Raw Blame History

PostgreSQL 15 数据库部署策略 - 摸底报告

文档版本: v1.1
创建日期: 2025-12-14
最后更新: 2025-12-14
数据库版本: PostgreSQL 15.14 (Docker: postgres:15-alpine)
目标环境: 阿里云 RDS PostgreSQL 15
报告类型: 技术摸底 + 部署策略


📝 版本修订记录

v1.1 (2025-12-14) - 专业建议修正版

修正要点:

  1. pg-boss表"自愈"机制澄清

    • 修正pg-boss会在应用启动时自动创建表只要权限够
    • 降低严重性:从"严重差异"改为"自愈机制(无需担心)"
    • 增加说明pg-boss的智能自愈能力和权限要求
  2. 白名单配置强化说明

    • 新增必须使用VPC网段不能用单机IP
    • 示例172.16.0.0/12SAE的VPC网段
    • 原因SAE实例IP会变化单机IP会导致连接失败
    • 位置RDS准备、网络安全、下一步行动多处强调
  3. 备份策略简化(更务实)

    • 修正初期只需RDS自动备份含日志备份
    • 降级pg_dump ECS脚本从"第三道防线"降为"可选(业务做大后再考虑)"
    • 理由RDS自动备份+PITR已足够省心省力
    • 保留:脚本示例作为参考,标注"⚠️ 仅供参考,初期不需要"

贡献者: 项目技术团队反馈



📋 目录

  1. 本地数据库真实情况
  2. Prisma与数据库的差异分析
  3. 代码如何连接数据库
  4. 首次部署方案
  5. 未来更新策略
  6. RDS备份策略
  7. 最佳实践与禁止操作

本地数据库真实情况

1. 基础信息

Docker镜像postgres:15-alpine
数据库名称ai_clinical_research
连接信息postgresql://postgres:postgres@localhost:5432/ai_clinical_research
数据库大小26 MB测试/开发环境)
用户数据3个用户账号

2. Schema隔离架构10个Schema

你的数据库已经成功实施了10个Schema隔离架构

# Schema名称 表数量 状态 说明
1 platform_schema 8个表 已实施 平台基础设施用户、pg-boss队列
2 aia_schema 5个表 已实施 AI智能问答项目、对话、消息
3 pkb_schema 5个表 已实施 个人知识库(知识库、文档、批处理)
4 asl_schema 6个表 已实施 AI智能文献文献筛选
5 dc_schema 6个表 已实施 数据清洗模板、提取任务、Tool C
6 admin_schema 0个表 📋 空Schema 运营管理(预留)
7 rvw_schema 0个表 📋 空Schema 审稿系统(预留)
8 ssa_schema 0个表 📋 空Schema 智能统计分析(预留)
9 st_schema 0个表 📋 空Schema 统计分析工具(预留)
10 common_schema 0个表 📋 空Schema 通用能力层(预留)
11 public 4个表 ⚠️ 旧表遗留 _prisma_migrations, admin_logs, review_tasks, users

总计34个表30个在隔离Schema中4个在public中

3. 详细表清单

3.1 platform_schema8个表

✅ users              - 用户表3条记录
✅ app_cache          - Postgres-Only缓存替代Redis
✅ job                - pg-boss任务表
✅ job_common         - pg-boss任务通用表
✅ queue              - pg-boss队列表
✅ schedule           - pg-boss定时任务表
✅ subscription       - pg-boss订阅表
✅ version            - pg-boss版本表

⚠️ 重要发现pg-boss的6个表job/queue等是自动创建的不在Prisma Schema中

3.2 aia_schema5个表

✅ projects                  - 项目管理2条记录
✅ conversations             - 项目对话
✅ messages                  - 对话消息
✅ general_conversations     - 通用对话
✅ general_messages          - 通用消息

3.3 pkb_schema5个表

✅ knowledge_bases    - 知识库2条记录
✅ documents          - 文档
✅ batch_tasks        - 批处理任务
✅ batch_results      - 批处理结果976 KB
✅ task_templates     - 任务模板

3.4 asl_schema6个表

✅ screening_projects              - 文献筛选项目18条记录
✅ literatures                     - 文献数据2.9 MB
✅ screening_results               - 筛选结果1.2 MB
✅ screening_tasks                 - 筛选任务
✅ fulltext_screening_tasks        - 全文筛选任务
✅ fulltext_screening_results      - 全文筛选结果

3.5 dc_schema6个表

✅ dc_health_checks       - 健康检查
✅ dc_templates           - 数据清洗模板4条记录424 KB
✅ dc_extraction_tasks    - 提取任务728 KB
✅ dc_extraction_items    - 提取项6.5 MB最大表
✅ dc_tool_c_sessions     - Tool C会话960 KB
✅ dc_tool_c_ai_history   - Tool C AI历史1 MB

3.6 public schema4个表⚠️

⚠️ _prisma_migrations   - Prisma迁移记录6条记录
⚠️ admin_logs          - 管理日志(遗留)
⚠️ review_tasks        - 审查任务632 KB遗留
⚠️ users               - 用户表遗留与platform_schema.users重复

说明:

  • public.users 是Schema迁移前的旧表platform_schema.users 结构完全相同
  • admin_logsreview_tasks 应该迁移到 admin_schemarvw_schema
  • _prisma_migrations 应该保留在 public这是Prisma的标准位置

4. 数据量统计Top 15大表

Schema 表名 大小 说明
dc_schema dc_extraction_items 6.5 MB 数据清洗提取项(最大表)
asl_schema literatures 2.9 MB 文献数据
asl_schema screening_results 1.2 MB 筛选结果
dc_schema dc_tool_c_ai_history 1 MB Tool C AI历史
pkb_schema batch_results 976 KB 批处理结果
dc_schema dc_tool_c_sessions 960 KB Tool C会话
dc_schema dc_extraction_tasks 728 KB 提取任务
public review_tasks 632 KB 审查任务(遗留)
dc_schema dc_templates 424 KB 数据清洗模板
pkb_schema documents 296 KB 文档
aia_schema general_messages 208 KB 通用消息
asl_schema screening_projects 144 KB 筛选项目
aia_schema conversations 112 KB 对话
pkb_schema batch_tasks 112 KB 批处理任务
aia_schema messages 104 KB 消息

总结:数据主要集中在 DC数据清洗和 ASL文献筛选模块。

5. Prisma迁移历史

-- 6次Prisma迁移记录按时间顺序
20251010075003_init                             -- 2025-10-12初始化
20251010122727_add_conversation_metadata        -- 2025-10-12对话元数据
20251012124747_add_batch_processing_module      -- 2025-10-12批处理模块
20251014120128_add_review_tasks                 -- 2025-10-14审查任务
20251127_add_dc_tool_b_tables                   -- 2025-11-27DC Tool B
20251208_add_column_mapping                     -- 2025-12-08列映射

说明:

  • 最早的迁移init创建的是 public schema 中的表
  • 后续通过手工SQL迁移到了10个隔离Schema
  • Prisma并没有记录Schema迁移过程这些是手工SQL完成的

Prisma与数据库的差异分析

1. pg-boss表的"自愈"机制(无需担心)

现象: platform_schema 有 8 个表,但 Prisma Schema 只定义了 2 个:

  • Prisma定义AppCache, User
  • 🔧 pg-boss自动管理job, job_common, queue, schedule, subscription, version共6个表

原因: 这6个表是 pg-boss 库在应用启动时自动创建用于Postgres-Only任务队列不需要在Prisma Schema中定义。

pg-boss的"自愈"能力:

// backend启动时pg-boss会自动检查表是否存在
const boss = new PgBoss(process.env.DATABASE_URL)
await boss.start()
// ✅ 如果表不存在pg-boss会自动创建
// ✅ 只要数据库用户如aiclinical_rw有 CREATE TABLE 权限
// ✅ 完全不需要手工干预

影响评估:

  • 运行时零影响pg-boss自己管理这些表不通过Prisma访问
  • 首次部署自动创建RDS部署时backend启动会自动创建这6个表
  • ⚠️ Prisma db pull会检测到:执行 npx prisma db pull 时会发现"未定义的表"(可忽略)
  • ⚠️ Prisma migrate不操作:迁移不会同步这些表(这是正确的)

最佳实践:

// backend/prisma/schema.prisma
// 在文件开头添加注释(文档用途)

// ==================== pg-boss 自动管理的表(不需要定义) ====================
// 以下6个表由pg-boss库自动创建和维护请勿手工修改
// - platform_schema.job          - 任务表
// - platform_schema.job_common   - 任务通用配置
// - platform_schema.queue        - 队列表
// - platform_schema.schedule     - 定时任务表
// - platform_schema.subscription - 订阅表
// - platform_schema.version      - 版本表
// ==================== 以上表自动管理无需Prisma定义 ====================

结论pg-boss表"缺失"是正常的,无需担心!

2. public schema遗留表⚠️(需要清理)

问题: public schema 有 4 个表:

  • _prisma_migrationsPrisma标准表正确
  • users:与 platform_schema.users 重复
  • admin_logs:应迁移到 admin_schema
  • review_tasks:应迁移到 rvw_schema

影响:

  • ⚠️ 数据不一致风险(如果代码误读 public.users
  • ⚠️ 混淆(两个 users 表)
  • ⚠️ 空间浪费(重复数据)

解决方案:

-- 步骤1迁移 admin_logs 到 admin_schema
ALTER TABLE public.admin_logs SET SCHEMA admin_schema;

-- 步骤2迁移 review_tasks 到 rvw_schema
ALTER TABLE public.review_tasks SET SCHEMA rvw_schema;

-- 步骤3验证 platform_schema.users 和 public.users 数据一致后删除
-- (先在生产环境验证一周,确保代码不再引用 public.users
DROP TABLE public.users;

3. Prisma Schema的定义状态

Schema Prisma定义的模型 实际数据库表 状态
platform_schema 2个AppCache, User 8个+6个pg-boss自动管理 正常pg-boss自愈
aia_schema 5个 5个 一致
pkb_schema 5个 5个 一致
asl_schema 6个 6个 一致
dc_schema 6个 6个 一致
public 2个AdminLog, ReviewTask 4个+_prisma_migrations, users ⚠️ 需清理

结论:

  • Prisma Schema 完全准确pg-boss的6个表会自动创建不需要定义
  • ⚠️ public schema需要清理:遗留了旧的 users 表(首次部署后清理)

代码如何连接数据库

1. 环境变量配置

1.1 默认连接字符串env.ts

// AIclinicalresearch/backend/src/config/env.ts (line 50)
databaseUrl: process.env.DATABASE_URL || 'postgresql://postgres:postgres@localhost:5432/ai_clinical'

⚠️ 注意:

  • 默认值是 ai_clinical(不存在)
  • 实际数据库名是 ai_clinical_research
  • 说明你的 .env 文件中已经正确配置了 DATABASE_URL

1.2 连接池配置database.ts

// AIclinicalresearch/backend/src/config/database.ts
export const prisma = new PrismaClient({
  datasources: {
    db: {
      url: process.env.DATABASE_URL,  // 从环境变量读取
    },
  },
})

// 连接池计算公式
connectionLimit = Math.floor(RDS_MAX_CONNECTIONS / MAX_INSTANCES) - 10
// 示例400 / 20 - 10 = 10个连接/实例

云原生连接池策略:

DATABASE_URL=postgresql://user:pass@host:5432/db?connection_limit=10&pool_timeout=10

2. 连接方式

连接参数 本地开发 阿里云SAE
主机 localhost RDS内网地址如 rm-xxxxx.pg.rds.aliyuncs.com
端口 5432 5432
数据库 ai_clinical_research ai_clinical_research保持一致
用户名 postgres 自定义(如 aiclinical_rw
密码 postgres 强密码RDS创建时设置
连接限制 无限制 connection_limit=10SAE每实例
连接超时 默认 pool_timeout=10

3. 多Schema访问Prisma

// AIclinicalresearch/backend/prisma/schema.prisma
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
  schemas  = ["platform_schema", "aia_schema", "pkb_schema", "asl_schema", 
              "common_schema", "dc_schema", "rvw_schema", "admin_schema", 
              "ssa_schema", "st_schema", "public"]
}

generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["multiSchema"]  // 启用多Schema支持
}

代码中的使用:

// 自动路由到正确的Schema
await prisma.user.findMany()  // 访问 platform_schema.users
await prisma.project.findMany()  // 访问 aia_schema.projects

4. 原生SQL访问跨Schema

// 支持跨Schema查询
const result = await prisma.$queryRaw`
  SELECT u.name, p.name AS project_name
  FROM platform_schema.users u
  JOIN aia_schema.projects p ON p.user_id = u.id
`

5. 优雅关闭机制

// AIclinicalresearch/backend/src/config/database.ts
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'))  // SAE实例停止
process.on('SIGINT', () => gracefulShutdown('SIGINT'))    // Ctrl+C

async function gracefulShutdown(signal: string): Promise<void> {
  await prisma.$disconnect()  // 关闭所有连接
  process.exit(0)
}

这是云原生最佳实践防止SAE扩容/缩容时连接泄漏。


首次部署方案

方案对比

方案 方法 优点 缺点 推荐度
方案A pg_dump 全量导入 100%完整包括pg-boss表
结构+数据+索引+外键
一次性完成
需要手工清理测试数据
包含遗留的public表
强烈推荐
方案B Prisma Migrate Deploy 版本化管理
可重复执行
缺少pg-boss表运行时会报错
需要手工补充
⚠️ 不推荐(不完整)
方案C 手工SQL脚本 完全可控 工作量大
容易出错
⚠️ 不推荐(费时费力)

推荐方案Apg_dump全量导入详细步骤

步骤1本地数据库清理可选

# 如果本地有测试数据,可以选择清理
docker exec -it ai-clinical-postgres psql -U postgres -d ai_clinical_research

# 删除测试用户(保留真实用户)
DELETE FROM platform_schema.users WHERE email LIKE '%test%';

# 清理遗留的 public.usersSchema迁移后的旧表
DROP TABLE IF EXISTS public.users;

步骤2导出数据库包含结构+数据)

# 完整导出包括所有Schema、表、数据、索引、外键
docker exec ai-clinical-postgres pg_dump -U postgres -d ai_clinical_research \
  --format=plain \
  --no-owner \
  --no-acl \
  --encoding=UTF8 \
  > D:\MyCursor\ai_clinical_research_backup_$(Get-Date -Format 'yyyyMMdd_HHmmss').sql

# 文件大小应该在 100-200 KB当前数据量26MB

参数说明:

  • --format=plain纯文本SQL方便查看和编辑
  • --no-owner不导出所有者信息避免RDS用户名不匹配
  • --no-acl不导出权限信息使用RDS默认权限
  • --encoding=UTF8UTF-8编码中文支持

步骤3RDS PostgreSQL 15 准备

# 1. 在阿里云控制台创建RDS PostgreSQL 15实例
规格rds.pg.s2.large2核4GB入门级
存储100GB SSD支持自动扩容
版本PostgreSQL 15
网络VPC与SAE在同一VPC
白名单:⚠️ 必须配置VPC网段不能用单机IP
       - 示例172.16.0.0/12SAE的VPC网段
       - 查看方式SAE控制台 > 应用详情 > 网络配置 > VPC网段
       - ❌ 错误示例172.16.1.23单机IPSAE实例IP会变化

# 2. 创建数据库
CREATE DATABASE ai_clinical_research
  WITH ENCODING='UTF8'
  LC_COLLATE='en_US.UTF-8'
  LC_CTYPE='en_US.UTF-8'
  TEMPLATE=template0;

# 3. 创建应用用户(不要用超级用户)
CREATE USER aiclinical_rw WITH PASSWORD '你的强密码';
GRANT ALL PRIVILEGES ON DATABASE ai_clinical_research TO aiclinical_rw;
GRANT ALL ON SCHEMA public TO aiclinical_rw;

步骤4导入到RDS

# 方法1从本地直接导入需要RDS公网地址
psql -h rm-xxxxx.pg.rds.aliyuncs.com \
     -p 5432 \
     -U aiclinical_rw \
     -d ai_clinical_research \
     -f ai_clinical_research_backup_20251214_150000.sql

# 方法2通过ECS跳板机导入推荐更安全
scp backup.sql root@your-ecs-ip:/tmp/
ssh root@your-ecs-ip
psql -h rm-xxxxx.pg.rds.aliyuncs.com -U aiclinical_rw -d ai_clinical_research -f /tmp/backup.sql

步骤5验证导入结果

-- 1. 验证Schema数量
SELECT nspname FROM pg_namespace WHERE nspname LIKE '%_schema' ORDER BY nspname;
-- 应该有10个Schema

-- 2. 验证表数量
SELECT schemaname, COUNT(*) as table_count 
FROM pg_tables 
WHERE schemaname NOT IN ('pg_catalog', 'information_schema') 
GROUP BY schemaname 
ORDER BY schemaname;
-- platform_schema: 8, aia_schema: 5, pkb_schema: 5, asl_schema: 6, dc_schema: 6

-- 3. 验证数据量
SELECT COUNT(*) FROM platform_schema.users;  -- 应该有3条或你保留的数量
SELECT COUNT(*) FROM aia_schema.projects;    -- 应该有2条
SELECT COUNT(*) FROM asl_schema.literatures; -- 应该有对应数量

-- 4. 验证pg-boss表关键
SELECT tablename FROM pg_tables WHERE schemaname = 'platform_schema' ORDER BY tablename;
-- 应该包括job, job_common, queue, schedule, subscription, version

-- 5. 验证外键约束
SELECT conname, conrelid::regclass, confrelid::regclass 
FROM pg_constraint 
WHERE contype = 'f' 
LIMIT 10;

步骤6配置SAE环境变量

# 在SAE应用配置中添加
DATABASE_URL=postgresql://aiclinical_rw:强密码@rm-xxxxx.pg.rds.aliyuncs.com:5432/ai_clinical_research?connection_limit=10&pool_timeout=10&connect_timeout=10

# 其他相关配置
DB_MAX_CONNECTIONS=400    # RDS最大连接数
MAX_INSTANCES=20          # SAE最大实例数
NODE_ENV=production

步骤7应用启动验证

// backend启动时会自动执行
npm run build
npm run start

// 日志输出应该显示:
// ✅ 数据库连接成功!
// 📊 数据库版本: PostgreSQL 15.x
// 📊 当前数据库连接数: 1

步骤8Prisma Client生成重要

# 在backend目录下
cd backend
npx prisma generate

# 验证生成的Client
ls node_modules/.prisma/client/
# 应该包含所有Schema的类型定义

⚠️ 方案B为什么不推荐Prisma Migrate的局限

# 如果执行 prisma migrate deploy
cd backend
npx prisma migrate deploy

# 问题1缺少 pg-boss 表
# prisma migrate deploy 只会创建 Prisma Schema 中定义的表
# 不会创建 pg-boss 的 6 个表job, queue等

# 问题2应用启动时报错
# Error: relation "platform_schema.job" does not exist
# 因为代码中使用了 pg-boss但表不存在

# 问题3需要手工补救
# 必须手工执行 pg-boss 的初始化SQL
# 非常繁琐且容易出错

结论:pg_dump 全量导入是首次部署的最佳方案!


未来更新策略

场景1新增一个表或修改一个字段小更新

推荐方案Prisma Migrate版本化管理

# 步骤1在本地开发环境修改 schema.prisma
# 示例:为 User 表添加一个字段
model User {
  id        String  @id @default(uuid())
  email     String  @unique
  // ... 其他字段 ...
  phoneNumber String? @map("phone_number")  // 新增字段
  
  @@map("users")
  @@schema("platform_schema")
}

# 步骤2创建迁移本地
cd backend
npx prisma migrate dev --name add_user_phone_number

# 生成的迁移文件示例:
# backend/prisma/migrations/20251214120000_add_user_phone_number/migration.sql
ALTER TABLE "platform_schema"."users" ADD COLUMN "phone_number" TEXT;

# 步骤3测试本地迁移
npm run dev
# 验证新字段可用

# 步骤4提交到Git
git add backend/prisma/migrations/20251214120000_add_user_phone_number
git commit -m "feat: add phone_number to User model"
git push

# 步骤5部署到SAE自动执行迁移
# 在 SAE 的应用启动命令中添加:
# CMD ["sh", "-c", "npx prisma migrate deploy && node dist/index.js"]

# ⚠️ 注意Dockerfile 的 CMD 应该是:
CMD ["sh", "-c", "node dist/index.js"]
# 不要在生产启动时执行 migrate deploy风险太大

# 步骤6手工执行迁移推荐
# 方法1通过ECS跳板机执行
ssh root@your-ecs-ip
psql -h rm-xxxxx.pg.rds.aliyuncs.com -U aiclinical_rw -d ai_clinical_research \
     -f migrations/20251214120000_add_user_phone_number/migration.sql

# 方法2使用 RDS 控制台的 SQL 窗口
# 直接粘贴迁移SQL执行

# 步骤7验证
SELECT column_name, data_type 
FROM information_schema.columns 
WHERE table_schema = 'platform_schema' 
  AND table_name = 'users' 
  AND column_name = 'phone_number';

为什么不推荐在启动命令中执行 prisma migrate deploy

  1. 并发风险SAE有多个实例可能同时执行迁移
  2. 回滚困难:如果迁移失败,应用已经启动
  3. 数据安全:生产数据库应该手工迁移,有备份和回滚计划

场景2新增一个大模块如SSA智能统计分析

推荐方案:结构化迁移流程

# 步骤1在本地开发环境设计Schema
# 示例SSA智能统计分析模块
# backend/prisma/schema.prisma

// SSA Schema新增
model SsaAnalysisTask {
  id        String   @id @default(uuid())
  userId    String   @map("user_id")
  projectId String   @map("project_id")
  taskName  String   @map("task_name")
  status    String   @default("pending")
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @updatedAt @map("updated_at")
  
  user      User     @relation(fields: [userId], references: [id])
  results   SsaAnalysisResult[]
  
  @@map("analysis_tasks")
  @@schema("ssa_schema")
}

model SsaAnalysisResult {
  id          String   @id @default(uuid())
  taskId      String   @map("task_id")
  resultType  String   @map("result_type")
  resultData  Json     @map("result_data")
  createdAt   DateTime @default(now()) @map("created_at")
  
  task        SsaAnalysisTask @relation(fields: [taskId], references: [id])
  
  @@map("analysis_results")
  @@schema("ssa_schema")
}

# 步骤2创建迁移
npx prisma migrate dev --name add_ssa_module

# 步骤3验证迁移SQL
# backend/prisma/migrations/20251214_add_ssa_module/migration.sql
-- 检查:
-- 1. 是否在 ssa_schema 中创建表
-- 2. 外键是否正确引用 platform_schema.users
-- 3. 索引是否完整

# 步骤4本地测试
# 开发SSA模块的业务逻辑
# 确保所有CRUD操作正常

# 步骤5准备生产部署
# 5.1 数据库备份(强制)
# 在RDS控制台手动创建快照ai_clinical_research_before_ssa_module_20251214

# 5.2 准备回滚脚本
# backend/prisma/migrations/20251214_add_ssa_module/rollback.sql
DROP TABLE IF EXISTS ssa_schema.analysis_results CASCADE;
DROP TABLE IF EXISTS ssa_schema.analysis_tasks CASCADE;

# 步骤6生产环境迁移分步执行
# 6.1 停机窗口(可选,如果无法零停机)
# 晚上11点-凌晨2点用户最少

# 6.2 执行迁移SQL
psql -h rm-xxxxx.pg.rds.aliyuncs.com -U aiclinical_rw -d ai_clinical_research \
     -f migrations/20251214_add_ssa_module/migration.sql

# 6.3 验证表结构
\dt ssa_schema.*
\d ssa_schema.analysis_tasks
\d ssa_schema.analysis_results

# 6.4 验证外键
SELECT conname, conrelid::regclass, confrelid::regclass 
FROM pg_constraint 
WHERE connamespace = 'ssa_schema'::regnamespace;

# 步骤7部署应用代码
# 7.1 更新backend
git pull
npm run build
# SAE自动部署或手动触发部署

# 7.2 健康检查
curl https://your-backend.sae.aliyuncs.com/health
# 应该返回 200 OK

# 步骤8烟雾测试Smoke Testing
# 8.1 测试SSA模块基本功能
POST /api/v1/ssa/analysis-tasks
GET /api/v1/ssa/analysis-tasks/:id

# 8.2 测试旧模块(回归测试)
GET /api/v1/aia/projects
GET /api/v1/pkb/knowledge-bases
# 确保旧功能不受影响

# 步骤9监控24小时
# 9.1 RDS监控
# - 连接数是否正常
# - CPU/内存使用率
# - 慢查询日志

# 9.2 SAE监控
# - 应用日志(错误数量)
# - API响应时间
# - 实例健康状态

# 步骤10如果失败立即回滚
# 10.1 回滚数据库
psql -h rm-xxxxx.pg.rds.aliyuncs.com -U aiclinical_rw -d ai_clinical_research \
     -f migrations/20251214_add_ssa_module/rollback.sql

# 10.2 回滚应用代码
# 在SAE控制台选择上一个版本点击"回滚"

# 10.3 恢复RDS快照如果必要
# 在RDS控制台恢复快照ai_clinical_research_before_ssa_module_20251214

场景3紧急修复Hotfix

# 示例production环境发现某个字段长度不够

# 步骤1本地创建紧急迁移
# backend/prisma/schema.prisma
model User {
  // 将 email 字段长度从 varchar(255) 改为 text
  email String @unique  // Prisma默认是text无需修改
  // ...
}

# 步骤2生成迁移
npx prisma migrate dev --name hotfix_user_email_length

# 步骤3测试迁移SQL本地
# 确保不会丢失数据

# 步骤4生产环境执行快速通道
# 4.1 备份(快照)
# RDS控制台 > 备份 > 立即备份 > 备注hotfix_before_email_length

# 4.2 执行SQL通过SQL窗口秒级完成
ALTER TABLE platform_schema.users ALTER COLUMN email TYPE TEXT;

# 4.3 验证
SELECT column_name, data_type, character_maximum_length
FROM information_schema.columns
WHERE table_schema = 'platform_schema' 
  AND table_name = 'users' 
  AND column_name = 'email';

# 4.4 提交Git
git add backend/prisma/migrations/20251214_hotfix_user_email_length
git commit -m "hotfix: increase user email column length"
git push

RDS备份策略

1. 阿里云RDS自动备份强烈推荐

1.1 配置自动备份

# 在RDS控制台配置
备份设置 > 备份策略

# 推荐配置:
数据备份保留时间7天免费额度
日志备份保留时间7天
备份周期:每天一次
备份时间凌晨2:00-4:00业务低峰期
备份方式:物理备份(快,占用空间小)

优势:

  • 全自动,无需人工干预
  • 时间点恢复Point-in-Time RecoveryPITR可恢复到任意秒
  • 存储在OSS独立于RDS实例
  • 免费7天内

1.2 手动快照(重要操作前)

# 场景:
# - 大版本升级前
# - 数据库Schema重大变更前
# - 删除大量数据前

# 操作:
RDS控制台 > 备份恢复 > 备份实例 > 创建备份
备份方式:物理备份
备注migration_before_ssa_module_20251214

快照保留建议:

  • 迁移前快照保留30天
  • 版本升级前快照保留60天
  • 季度归档快照保留1年

2. 逻辑备份(pg_dump- 可选策略(业务做大后再考虑)

⚠️ 初期建议不需要自己写脚本RDS自动备份就够了

阶段 备份策略 理由
初期(当前) 只用RDS自动备份 - 自动备份+日志备份=PITR任意时间点恢复
- 阿里云托管,稳定可靠
- 无需维护脚本和ECS
成长期 可选:增加异地灾备 - 业务关键后,考虑多地域备份
- 使用RDS的"跨地域备份"功能(一键配置)
成熟期 可选:自定义备份 - 需要特殊备份策略(如每小时备份)
- 需要长期归档如5年

如果未来需要自定义备份,参考脚本:

# ⚠️ 仅供参考,初期不需要执行
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/data/pg_backups"
RDS_HOST="rm-xxxxx.pg.rds.aliyuncs.com"
RDS_USER="aiclinical_rw"
RDS_DB="ai_clinical_research"

# 导出SQL
pg_dump -h $RDS_HOST -U $RDS_USER -d $RDS_DB \
  --format=plain \
  --no-owner \
  --no-acl \
  --encoding=UTF8 \
  --file=$BACKUP_DIR/ai_clinical_research_$DATE.sql

# 压缩并上传到OSS
gzip $BACKUP_DIR/ai_clinical_research_$DATE.sql
ossutil cp $BACKUP_DIR/ai_clinical_research_$DATE.sql.gz \
  oss://your-bucket/database-backups/

# 清理本地备份
ls -t $BACKUP_DIR/*.sql.gz | tail -n +4 | xargs rm -f

总结初期专注RDS自动备份省心省力

3. 恢复演练(每季度一次)

# 目的:验证备份可用,熟悉恢复流程

# 步骤1创建测试RDS实例
# RDS控制台 > 创建实例 > 按备份创建
# 选择最新的自动备份或快照

# 步骤2验证数据完整性
psql -h test-rm-xxxxx.pg.rds.aliyuncs.com -U aiclinical_rw -d ai_clinical_research

# 检查:
SELECT schemaname, COUNT(*) FROM pg_tables 
WHERE schemaname NOT IN ('pg_catalog', 'information_schema') 
GROUP BY schemaname;

SELECT COUNT(*) FROM platform_schema.users;

# 步骤3记录恢复时间
# 26MB数据库完整恢复约5-10分钟

# 步骤4删除测试实例

4. 备份监控与告警

# 在阿里云监控配置告警规则

# 规则1备份失败告警
资源RDS实例
指标:备份任务状态
条件:备份失败
通知:钉钉群 + 邮件

# 规则2备份空间不足告警
资源RDS实例
指标:备份使用空间
条件:>80%
通知:邮件

# 规则3超过24小时未备份告警
资源RDS实例
指标:最后备份时间
条件:>24小时
通知:紧急(短信+电话)

最佳实践与禁止操作

最佳实践11条黄金法则

1. 连接池管理

// ✅ 正确:在 DATABASE_URL 中配置连接限制
DATABASE_URL=postgresql://user:pass@host:5432/db?connection_limit=10&pool_timeout=10

// ✅ 正确根据SAE实例数动态计算
connectionLimit = Math.floor(400 / 20) - 10 = 10

// ❌ 错误不配置连接限制会耗尽RDS连接
DATABASE_URL=postgresql://user:pass@host:5432/db

2. 优雅关闭

// ✅ 正确:监听 SIGTERM 信号
process.on('SIGTERM', async () => {
  await prisma.$disconnect()
  process.exit(0)
})

// ❌ 错误不关闭连接SAE缩容时连接泄漏

3. 事务管理

// ✅ 正确使用Prisma事务
await prisma.$transaction(async (tx) => {
  await tx.user.create({ data: { email: 'test@example.com' } })
  await tx.project.create({ data: { userId: 'xxx', name: 'Test' } })
})

// ❌ 错误手动管理事务容易忘记COMMIT
await prisma.$executeRaw`BEGIN`
// ... 多个操作 ...
await prisma.$executeRaw`COMMIT`  // 如果中间出错,不会回滚

4. Schema隔离访问

// ✅ 正确Prisma自动路由到正确的Schema
await prisma.user.findMany()  // 自动访问 platform_schema.users

// ✅ 正确跨Schema查询外键关系
await prisma.user.findUnique({
  where: { id: 'xxx' },
  include: { projects: true }  // 自动JOIN aia_schema.projects
})

// ❌ 错误手写Schema名称Prisma不推荐
await prisma.$queryRaw`SELECT * FROM platform_schema.users`  // 可以工作,但类型不安全

5. 索引优化

-- ✅ 正确:为频繁查询的字段创建索引
CREATE INDEX idx_users_email ON platform_schema.users(email);
CREATE INDEX idx_projects_user_id ON aia_schema.projects(user_id);

-- ✅ 正确为外键创建索引Prisma不自动创建
CREATE INDEX idx_conversations_project_id ON aia_schema.conversations(project_id);

-- ❌ 错误:创建过多索引(影响写入性能)
-- 只为频繁查询的字段创建索引

6. 迁移测试

# ✅ 正确:先在测试环境验证
# 测试环境 > 预发环境 > 生产环境

# ✅ 正确:准备回滚脚本
# 每个迁移都有对应的rollback.sql

# ❌ 错误:直接在生产环境执行未测试的迁移

7. 备份验证

# ✅ 正确:每季度恢复演练
# 确保备份可用

# ✅ 正确:重要操作前手动快照
# 大版本升级、Schema变更前

# ❌ 错误:从不验证备份(备份失效时才发现)

8. 监控告警

# ✅ 正确:监控关键指标
- RDS连接数告警阈值80%
- 慢查询(告警阈值:>1秒
- 死锁(告警阈值:>0
- CPU使用率告警阈值>70%

# ❌ 错误:不监控(问题发生后才发现)

9. 密码管理

# ✅ 正确使用强密码16位+大小写+数字+符号)
DATABASE_URL=postgresql://user:Abc123!@#XYZ456@host:5432/db

# ✅ 正确定期轮换密码每6个月

# ❌ 错误使用弱密码postgres/123456

10. 网络安全

# ✅ 正确RDS只允许VPC内网访问
# 白名单配置必须使用VPC网段不能用单机IP
白名单示例172.16.0.0/12SAE的VPC网段
获取方式SAE控制台 > 应用详情 > 网络配置 > VPC网段

# ✅ 正确:不开放公网访问(除非临时调试)

# ❌ 错误1白名单配置 0.0.0.0/0全世界可访问
# ❌ 错误2白名单配置单机IP 172.16.1.23SAE实例IP会变化导致连接失败
# ❌ 错误3配置多个单机IP维护困难且无法应对SAE弹性扩容

11. pg-boss表管理

// ✅ 正确不要在Prisma Schema中定义pg-boss表
// pg-boss自动管理不需要手工定义

// ✅ 正确不要手工修改pg-boss表
// 可能导致任务队列异常

// ❌ 错误删除pg-boss表
// DROP TABLE platform_schema.job;  // 会导致应用崩溃

12. 时区统一配置

-- ✅ 正确RDS时区配置为 Asia/Shanghai
-- RDS控制台 > 参数设置 > timezone
timezone = Asia/Shanghai

-- ✅ 验证时区
SHOW timezone;
-- 应该显示Asia/Shanghai

-- ❌ 错误使用UTC时区与应用时区不一致
-- 后果:
-- 1. 日志时间对不上前端14:00数据库06:00
-- 2. pg-boss定时任务在错误时间触发
-- 3. 用户看到的时间戳错误

配置步骤:

# 步骤1登录RDS控制台
# 阿里云 > 云数据库RDS > PostgreSQL实例

# 步骤2修改参数
# 实例详情 > 参数设置 > 搜索 "timezone"
# 修改为Asia/Shanghai

# 步骤3重启实例参数修改需要重启
# 实例详情 > 重启实例

# 步骤4验证
psql -h rm-xxxxx.pg.rds.aliyuncs.com -U aiclinical_rw -d ai_clinical_research
SHOW timezone;
# 应该显示Asia/Shanghai

与应用时区保持一致:

# Node.js后端ENV TZ=Asia/Shanghai
# Python微服务ENV TZ=Asia/Shanghai
# 前端NginxENV TZ=Asia/Shanghai
# RDS PostgreSQLtimezone = Asia/Shanghai
# ✅ 所有服务时区统一,避免时间混乱

绝对禁止的操作10条红线

🚫 1. 禁止在生产环境直接执行 DROP TABLE

-- ❌ 绝对禁止
DROP TABLE platform_schema.users;  -- 丢失所有用户数据,无法恢复

-- ✅ 正确做法先备份再重命名观察7天后删除
ALTER TABLE platform_schema.users RENAME TO users_deprecated_20251214;
-- 7天后确认无问题再删除
DROP TABLE platform_schema.users_deprecated_20251214;

🚫 2. 禁止在生产环境执行未测试的迁移

# ❌ 绝对禁止
psql -h production-rds -f untested_migration.sql  # 可能破坏数据

# ✅ 正确做法:测试环境 > 预发环境 > 生产环境

🚫 3. 禁止不备份就执行重大变更

-- ❌ 绝对禁止
ALTER TABLE platform_schema.users DROP COLUMN email;  -- 没有备份

-- ✅ 正确做法:先创建快照
-- RDS控制台 > 备份实例 > 创建备份
-- 然后再执行变更

🚫 4. 禁止在应用代码中硬编码数据库密码

// ❌ 绝对禁止
const prisma = new PrismaClient({
  datasources: {
    db: {
      url: 'postgresql://user:password@host:5432/db'  // 硬编码
    }
  }
})

// ✅ 正确做法:使用环境变量
const prisma = new PrismaClient({
  datasources: {
    db: {
      url: process.env.DATABASE_URL  // 环境变量
    }
  }
})

🚫 5. 禁止不配置连接池限制

# ❌ 绝对禁止
DATABASE_URL=postgresql://user:pass@host:5432/db  # 无限制

# ✅ 正确做法
DATABASE_URL=postgresql://user:pass@host:5432/db?connection_limit=10

🚫 6. 禁止在生产环境使用 prisma migrate dev

# ❌ 绝对禁止
npx prisma migrate dev  # 会删除数据重建

# ✅ 正确做法
npx prisma migrate deploy  # 只应用新迁移,不删除数据

🚫 7. 禁止在生产环境启用Prisma的 query 日志

// ❌ 绝对禁止(生产环境)
export const prisma = new PrismaClient({
  log: ['query', 'info', 'warn', 'error']  // 性能严重下降
})

// ✅ 正确做法(生产环境)
export const prisma = new PrismaClient({
  log: process.env.NODE_ENV === 'production' ? ['error'] : ['query', 'info', 'warn', 'error']
})

🚫 8. 禁止手工修改 _prisma_migrations

-- ❌ 绝对禁止
DELETE FROM _prisma_migrations WHERE migration_name = 'xxx';  -- 破坏迁移历史

-- ✅ 正确做法:如果迁移出错,回滚并修复
-- 不要手工修改 _prisma_migrations

🚫 9. 禁止在生产环境使用超级用户postgres

# ❌ 绝对禁止
DATABASE_URL=postgresql://postgres:password@host:5432/db  # 权限过大

# ✅ 正确做法:创建应用专用用户
CREATE USER aiclinical_rw WITH PASSWORD 'xxx';
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA platform_schema TO aiclinical_rw;

🚫 10. 禁止删除pg-boss的表

-- ❌ 绝对禁止
DROP TABLE platform_schema.job;  -- 应用立即崩溃
DROP TABLE platform_schema.queue;  -- 任务队列失效

-- ✅ 正确做法永远不要手工操作pg-boss表
-- 如果需要清理使用pg-boss的API

📝 总结与建议

1. 你的数据库现状(非常好)

维度 状态 评分
Schema隔离架构 10个Schema全部就位
Postgres-Only架构 pg-boss正常工作支持自愈
数据完整性 外键约束完整
Prisma Schema 完全准确pg-boss表会自动创建
数据量 26MB适合迁移
遗留问题 ⚠️ public schema有旧表首次部署后清理

总评你的数据库架构非常优秀可以直接部署到RDS

2. 首次部署推荐方案

🏆 强烈推荐pg_dump 全量导入

理由:
✅ 100%完整包括pg-boss表
✅ 结构+数据+索引+外键一次性完成
✅ 风险低,可重复执行
✅ 恢复快26MB约30秒

时间估算:
- 导出1分钟
- 传输到ECS1分钟
- 导入RDS3分钟
- 验证2分钟
总计:<10分钟

3. 未来更新推荐方案

更新类型 推荐方案 工具
小更新(新增字段) Prisma Migrate npx prisma migrate dev
中更新(新增表) Prisma Migrate npx prisma migrate dev
大更新(新模块) 结构化流程 Prisma + 手工SQL + 备份
紧急修复Hotfix 快速通道 直接SQL先备份

4. 备份策略(初期简化版)

第一道防线RDS自动备份必做⭐⭐⭐⭐⭐
├─ 数据备份每天凌晨2点
├─ 日志备份实时开启PITR
└─ 保留7天

第二道防线:手动快照(必做)⭐⭐⭐⭐
└─ 重大操作前创建

第三道防线pg_dump脚本初期不需要
└─ 业务做大后再考虑

恢复演练:每季度一次(必做)

初期只需配置RDS自动备份省心省力

5. 清理遗留问题(可选)

-- 建议在首次部署到RDS后清理
-- 1. 迁移 admin_logs 到 admin_schema
ALTER TABLE public.admin_logs SET SCHEMA admin_schema;

-- 2. 迁移 review_tasks 到 rvw_schema
ALTER TABLE public.review_tasks SET SCHEMA rvw_schema;

-- 3. 删除重复的 public.users验证后
-- 先对比数据一致性
SELECT COUNT(*) FROM public.users;
SELECT COUNT(*) FROM platform_schema.users;
-- 一致后删除
DROP TABLE public.users;

-- 4. 更新Prisma Schema
model AdminLog {
  // ...
  @@map("admin_logs")
  @@schema("admin_schema")  //  public 改为 admin_schema
}

model ReviewTask {
  // ...
  @@map("review_tasks")
  @@schema("rvw_schema")  //  public 改为 rvw_schema
}

6. 下一步行动

☐ 1. 创建阿里云RDS PostgreSQL 15实例
☐ 2. 配置VPC、白名单用VPC网段、备份策略自动备份+日志备份)
☐ 3. 本地清理测试数据(可选)
☐ 4. pg_dump导出数据库
☐ 5. 通过ECS跳板机导入RDS
☐ 6. 验证导入结果Schema、表、数据、pg-boss
☐ 7. 配置SAE环境变量DATABASE_URL
☐ 8. 部署backend应用
☐ 9. 端到端测试
☐ 10. 监控24小时

预计时间3-4小时从0到上线

文档创建人: AI助手
最后更新: 2025-12-14
版本: v1.0

核心理念:数据安全第一,充分备份,渐进式迁移,完整验证