# Schema隔离方案与成本分? > **文档版本?* v1.0 > **创建日期?* 2025-11-06 > **最后更新:** 2025-11-06 > **文档状态:** 架构分析 > **作者:** 技术架构师 --- ## 📋 核心问题 1. 什么是真正的Schema隔离? 2. 从逻辑隔离到物理隔离的改造成本有多高? 3. 现在做Schema隔离的成本高吗? 4. 当前业务体量下,需要现在做吗? 5. 最佳实施时机是什么时候? --- ## 🎯 两种隔离方式对比 ### 逻辑隔离(当前方案) **定义?* - 所有表都在同一个Schema(`public`)中 - 通过**表名前缀**来区分不同模? - 代码层面按模块组? **示例?* ```sql -- 所有表都在public schema public.users -- 用户? public.aia_projects -- AI问答模块的项目表 public.aia_conversations -- AI问答模块的对话表 public.asl_projects -- AI文献模块的项目表 public.asl_literature_items -- AI文献模块的文献表 public.pkb_knowledge_bases -- 知识库模块的知识库表 public.dc_projects -- 数据清洗模块的项目表 public.review_tasks -- 稿件审查模块的任务表 ``` **Prisma Schema示例?* ```prisma // 逻辑隔离:使用@@map指定表名 model AiaProject { id String @id @default(uuid()) userId String @map("user_id") name String // ... @@map("aia_projects") // 表名前缀标识模块 } model AslProject { id String @id @default(uuid()) userId String @map("user_id") name String // ... @@map("asl_projects") // 表名前缀标识模块 } ``` **查询方式?* ```typescript // 业务代码无感? const project = await prisma.aiaProject.findUnique({ where: { id: projectId } }); ``` --- ### 物理隔离(真正的Schema隔离? **定义?* - 为每个模块创?*独立的PostgreSQL Schema** - 表分散在不同的Schema? - 数据库层面真正隔? **示例?* ```sql -- 创建独立Schema CREATE SCHEMA platform_schema; -- 平台? CREATE SCHEMA aia_schema; -- AI问答模块 CREATE SCHEMA asl_schema; -- AI文献模块 CREATE SCHEMA pkb_schema; -- 知识库模? CREATE SCHEMA dc_schema; -- 数据清洗模块 CREATE SCHEMA review_schema; -- 稿件审查模块 CREATE SCHEMA admin_schema; -- 运营管理? -- 表分布在不同Schema platform_schema.users -- 用户? aia_schema.projects -- AI问答的项目表 aia_schema.conversations -- AI问答的对话表 asl_schema.projects -- AI文献的项目表 asl_schema.literature_items -- AI文献的文献表 pkb_schema.knowledge_bases -- 知识库表 dc_schema.projects -- 数据清洗的项目表 review_schema.tasks -- 稿件审查的任务表 ``` **Prisma Schema示例?* ```prisma // 物理隔离:指定schema model AiaProject { id String @id @default(uuid()) userId String @map("user_id") name String // ... @@map("projects") // 表名不需要前缀 @@schema("aia_schema") // 指定Schema ? } model AslProject { id String @id @default(uuid()) userId String @map("user_id") name String // ... @@map("projects") // 表名不需要前缀 @@schema("asl_schema") // 指定Schema ? } ``` **查询方式?* ```typescript // 业务代码无感知(Prisma会自动处理) const project = await prisma.aiaProject.findUnique({ where: { id: projectId } }); // 实际执行的SQL? // SELECT * FROM aia_schema.projects WHERE id = $1 ``` --- ## 📊 两种方案对比 | 维度 | 逻辑隔离(当前) | 物理隔离(目标) | |------|----------------|----------------| | **复杂?* | ?简?| ⭐⭐?中等 | | **实施难度** | ??| ⭐⭐?中等 | | **维护成本** | ⭐⭐ ?| ⭐⭐?中等 | | **隔离?* | ⭐⭐ 中等(代码层面) | ⭐⭐⭐⭐?高(数据库层面) | | **权限控制** | ⭐⭐ 中等 | ⭐⭐⭐⭐?高(数据库级别) | | **微服务拆?* | ⭐⭐?需要改?| ⭐⭐⭐⭐?易于拆分 | | **模块独立部署** | ⭐⭐ 困难 | ⭐⭐⭐⭐?容易 | | **跨模块查?* | ⭐⭐⭐⭐?容易(同一Schema?| ⭐⭐?需要跨Schema查询 | | **备份恢复** | ⭐⭐?整体备份 | ⭐⭐⭐⭐?可按Schema备份 | | **当前适用?* | ?适合当前阶段 | ⚠️ 未来阶段 | --- ## 💰 改造成本分? ### 从逻辑隔离到物理隔离需要改什么? #### 1. 数据库层面改? **步骤1:创建Schema** ```sql -- 创建新Schema(非常简单) CREATE SCHEMA platform_schema; CREATE SCHEMA aia_schema; CREATE SCHEMA asl_schema; CREATE SCHEMA pkb_schema; CREATE SCHEMA dc_schema; CREATE SCHEMA review_schema; CREATE SCHEMA admin_schema; ``` **工作量:** 10分钟 --- **步骤2:创建新表结?* ```sql -- 在新Schema中创建表 CREATE TABLE aia_schema.projects ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL, name VARCHAR(255) NOT NULL, -- ... 其他字段 -- 外键可能需要跨Schema CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES platform_schema.users(id) ); -- 类似的,为所有表创建新的表结? ``` **工作量:** - 自动生成(Prisma Migrate):1-2小时 - 手动创建?? --- **步骤3:数据迁?* ```sql -- 迁移数据(从public到新Schema? INSERT INTO aia_schema.projects SELECT * FROM public.aia_projects; INSERT INTO aia_schema.conversations SELECT * FROM public.aia_conversations; -- ... 为所有表迁移数据 ``` **工作量:** - 数据量小?100万行):1-2小时 - 数据量大?100万行):半天?? **风险?* - ⚠️ 需要停机(或做在线迁移? - ⚠️ 需要验证数据完整? - ⚠️ 外键约束可能有问? --- **步骤4:清理旧?* ```sql -- 删除旧表(确认无误后? DROP TABLE public.aia_projects; DROP TABLE public.aia_conversations; -- ... ``` **工作量:** 1小时 **总计数据库改造工作量?* - 自动化:半天?? - 手动?-3? --- #### 2. 代码层面改? **步骤1:修改Prisma Schema** **逻辑隔离(当前)?* ```prisma model AiaProject { id String @id @default(uuid()) userId String @map("user_id") name String @@map("aia_projects") // 需要前缀 } ``` **物理隔离(修改后):** ```prisma model AiaProject { id String @id @default(uuid()) userId String @map("user_id") name String @@map("projects") // 不需要前缀 @@schema("aia_schema") // 新增:指定Schema ? } ``` **工作量:** - 修改所有Model(约50-100个Model? - 时间?-4小时 --- **步骤2:处理外键和关联** **问题:跨Schema的外?* ```prisma // User在platform_schema model User { id String @id @default(uuid()) email String @@map("users") @@schema("platform_schema") } // Project在aia_schema model AiaProject { id String @id @default(uuid()) userId String @map("user_id") // 跨Schema关联 ⚠️ user User @relation(fields: [userId], references: [id]) @@map("projects") @@schema("aia_schema") } ``` **Prisma处理?* - ?Prisma会自动处理跨Schema的关? - ?生成的SQL会包含正确的Schema前缀 - ⚠️ 但需要测试验? **工作量:** - 测试所有跨Schema关联:半天到1? --- **步骤3:业务代码改?* **好消息:业务代码几乎不需要改?* ? ```typescript // 业务代码完全不变 const project = await prisma.aiaProject.findUnique({ where: { id: projectId }, include: { user: true // 跨Schema关联,Prisma自动处理 } }); ``` **原因?* - ?Prisma ORM抽象了底层Schema - ?业务代码只依赖Prisma Model,不直接写SQL **例外情况?* - ⚠️ 如果有原始SQL查询(`prisma.$queryRaw`),需要修? - ⚠️ 如果有数据库视图(View),需要修? - ⚠️ 如果有数据库函数(Function),需要修? **工作量:** - 业务代码?改造(如果没有原始SQL? - 原始SQL:需要逐个修改(估?0-20处) - 时间:半天到1? --- **步骤4:运行Prisma Migrate** ```bash # 生成新的迁移文件 npx prisma migrate dev --name schema-isolation # 或手动迁移(生产环境? npx prisma migrate deploy ``` **工作量:** - 开发环境:10分钟 - 生产环境:需要详细计划(半天准备? --- #### 3. 测试验证 **需要测试的内容?* 1. ?所有API接口是否正常 2. ?跨Schema关联是否正常(用?项目、项?对话等) 3. ?外键约束是否正常 4. ?数据完整性检? 5. ?性能测试(跨Schema查询性能? 6. ?备份恢复测试 **工作量:** - 单元测试:自动化?-2小时 - 集成测试:半? - 端到端测试:1? - 总计?-2? --- ### 总改造成? **开发环境:** | 任务 | 工作?| |------|-------| | 数据库Schema创建 | 10分钟 | | Prisma Schema修改 | 2-4小时 | | 数据迁移脚本 | 1-2小时 | | 原始SQL修改 | 半天 | | 测试验证 | 1-2?| | **总计** | **2-3?* | **生产环境?* | 任务 | 工作?| |------|-------| | 迁移方案设计 | 半天 | | 数据备份 | 1小时 | | 数据迁移 | 半天??| | 验证测试 | 1?| | 应急回滚方?| 半天 | | **总计** | **3-4?* | **风险评估?* - ⚠️ **风险等级:中?* - ⚠️ **主要风险:数据迁移失败、外键约束问题、停机时?* - ?**缓解措施:详细测试、回滚方案、灰度发?* --- ## 🤔 现在?vs 以后? ### 现在做的优势 ? 1. **数据量小,迁移快** ``` 当前数据量(估算): - 用户? 100 - 项目? 500 - 对话? 5,000 - 文档? 1,000 迁移时间? 1小时 风险:低 ``` 2. **业务复杂度低,测试简?* ``` 当前模块? - ?AIA(AI问答? 已完? - ?PKB(知识库? 已完? - ?ASL(AI文献? 未开? - ?DC、SSA、ST、RVW - 未开? 需要测试的模块? ``` 3. **为未来打下坚实基础** ``` 优势? - ?新模块(ASL、DC等)直接用物理隔? - ?避免未来大规模改? - ?支持模块独立部署 - ?支持微服务拆? ``` 4. **团队学习成本?* ``` 当前团队小: - 开发人员少,沟通成本低 - 代码改动影响范围? - 易于推广新规? ``` --- ### 现在做的劣势 ? 1. **需要投入时?* ``` 开发时间:2-3天(开发环境) 生产时间?-4天(生产环境? 总计?? 延迟:ASL模块开发延?? ``` 2. **有一定风?* ``` 风险? - 数据迁移失败 - 外键约束问题 - 需要停? 缓解:详细测试、回滚方? ``` 3. **当前没有紧迫需?* ``` 事实? - 没有微服务拆分需? - 没有模块独立部署需? - 没有私有化部署需? 结论:暂时不需要物理隔? ``` --- ### 以后做的优势 ? 1. **延迟投入** ``` 当前:专注业务开发(ASL、DC等) 未来:有需求时再改? 优势:快速推进业? ``` 2. **需求明确时再做** ``` 触发条件? - 需要微服务拆分 - 需要模块独立部? - 需要私有化部署 - 数据量大,需要性能优化 此时改造目标明? ``` --- ### 以后做的劣势 ? 1. **数据量大,迁移慢** ``` 未来数据量(估算): - 用户?0,000+ - 项目?00,000+ - 对话?,000,000+ - 文档?00,000+ 迁移时间:数小时到数? 风险:高 ``` 2. **业务复杂,测试困?* ``` 未来模块? - ?7个模块全部上? - ?复杂的跨模块关联 - ?大量用户在使? 测试难度:高 回滚成本:高 ``` 3. **需要停机或在线迁移** ``` 停机迁移? - 影响用户体验 - 可能丢失订单 在线迁移? - 技术复杂度? - 需要双写、数据同? ``` 4. **改造成本成倍增?* ``` 未来改造成本(估算): - 开发时间:1-2? - 测试时间?-2? - 生产迁移?? - 总计?-5? 现在?-5倍! ``` --- ## 🎯 建议与决? ### 方案A:现在做物理隔离 ⭐⭐⭐⭐?**强烈推荐** **适用场景?* - ??周时间投? - ?重视长期架构健康 - ?未来有模块独立部署需? - ?未来有私有化部署需? **理由?* 1. **成本最?*:数据量小,迁移快(< 1小时? 2. **风险最?*:业务简单,测试容易 3. **收益最?*:为未来7个模块打下坚实基础 4. **避免技术?*:避免未来大规模改? **实施计划?* ``` Week 1:Schema隔离改造(2-3天) Day 1-2:开发环境改? - 创建Schema - 修改Prisma Schema - 数据迁移脚本 - 测试验证 Day 3:生产环境迁? - 备份数据 - 执行迁移 - 验证测试 - 监控 Week 2:继续ASL模块开? ``` **投入产出比:** ⭐⭐⭐⭐?极高 --- ### 方案B:暂不做物理隔离,继续逻辑隔离 ⭐⭐?**可接?* **适用场景?* - ?时间紧迫,必须尽快上线ASL模块 - ?近期?个月内)没有微服务拆分需? - ?近期没有私有化部署需? **理由?* 1. **快速推进业?*:专注ASL模块开? 2. **逻辑隔离足够?*:当前阶段可以满足需? 3. **延迟改?*:未来有需求时再做 **但需要遵守纪律:** ⚠️ 非常重要 ``` 严格使用表名前缀? - aia_* - asl_* - pkb_* - dc_* - review_* - admin_* 为未来改造打基础 ``` **触发改造的条件?* 1. 需要微服务拆分 2. 需要模块独立部? 3. 需要私有化部署 4. 数据量超?00万行 **投入产出比:** ⭐⭐?中等 --- ### 方案C:混合方案(折中?⭐⭐⭐⭐ **推荐** **方案描述?* 1. **新模块使用物理隔?* - ASL、DC、SSA、ST、RVW等新模块直接用物理隔? - 从一开始就创建独立Schema 2. **老模块暂时保持逻辑隔离** - AIA、PKB等已完成模块暂时不动 - 等未来有需求时再迁? **实施计划?* ``` 立即? 1. 创建新Schema CREATE SCHEMA asl_schema; CREATE SCHEMA dc_schema; CREATE SCHEMA admin_schema; ... 2. ASL模块使用物理隔离 model AslProject { @@schema("asl_schema") } 未来?-12个月后)? 3. 迁移老模? - 迁移AIA到aia_schema - 迁移PKB到pkb_schema ``` **优势?* - ?立即投入小(只需创建Schema?0分钟? - ?新模块享受物理隔离的好处 - ?老模块延迟改造,风险? **投入产出比:** ⭐⭐⭐⭐ ? --- ## 📊 决策矩阵 | 方案 | 立即投入 | 未来成本 | 风险 | 灵活?| 推荐?| |------|---------|---------|------|-------|-------| | **方案A:现在做物理隔离** | ⭐⭐?中(1周) | ⭐⭐⭐⭐??| ⭐⭐⭐⭐ ?| ⭐⭐⭐⭐??| ⭐⭐⭐⭐?| | **方案B:继续逻辑隔离** | ⭐⭐⭐⭐??| ⭐⭐ ?| ⭐⭐??| ⭐⭐ ?| ⭐⭐?| | **方案C:混合方?* | ⭐⭐⭐⭐?低(10分钟?| ⭐⭐⭐⭐ ?| ⭐⭐⭐⭐ ?| ⭐⭐⭐⭐ ?| ⭐⭐⭐⭐ | --- ## 🎯 最终建? ### 我的推荐:方案A(现在做物理隔离)⭐⭐⭐⭐⭐ **理由?* **1. 成本最低的时间窗口** ``` 当前? - 数据量小? 1万行? - 迁移时间? 1小时 - 测试时间?-2? - 总成本:1? 6个月后: - 数据量大?00万行+? - 迁移时间:数小时到数? - 测试时间?-2? - 总成本:3-5? 成本差异?-5倍! ``` **2. 最佳学习机?* ``` 当前? - 团队小,沟通成本低 - 模块少,改造范围小 - 易于建立规范 未来? - 团队大,沟通成本高 - 模块多,改造范围大 - 难以统一规范 ``` **3. 避免技术债累?* ``` 技术债的特点? - 时间越久,利息越? - 改造成本成倍增? - 影响业务创新 现在改造: - 一次性还? - 轻装上阵 ``` **4. 为未来打下坚实基础** ``` 未来需求(6-12个月): - 审稿系统独立部署 - AI文献模块独立销? - 私有化部? - 微服务拆? 物理隔离是基础设施 ``` --- ### 如果必须选择方案B或C **推荐:方案C(混合方案)** **最低要求:** 1. ?立即创建所有Schema?0分钟? 2. ?新模块(ASL、DC等)使用物理隔离 3. ?老模块(AIA、PKB)延迟迁? **触发老模块迁移的条件?* - 数据量超?0万行 - 需要模块独立部? - 需要私有化部署 --- ## 📋 实施清单(方案A? ### 准备阶段?天) - [ ] 详细阅读Prisma Schema文档 - [ ] 设计Schema结构(platform_schema, aia_schema, asl_schema等) - [ ] 编写数据迁移脚本 - [ ] 准备测试用例 - [ ] 准备回滚方案 ### 开发环境改造(1-2天) - [ ] 创建所有Schema - [ ] 修改Prisma Schema(所有Model? - [ ] 运行Prisma Migrate - [ ] 迁移开发环境数? - [ ] 运行单元测试 - [ ] 运行集成测试 - [ ] 手动测试所有功? ### 生产环境迁移?天) - [ ] 备份数据? - [ ] 创建Schema - [ ] 执行数据迁移 - [ ] 验证数据完整? - [ ] 启动应用 - [ ] 监控错误日志 - [ ] 性能测试 ### 验收标准 - [ ] 所有API接口正常 - [ ] 跨Schema关联正常 - [ ] 外键约束正常 - [ ] 数据完整性正? - [ ] 性能无明显下? - [ ] 无错误日? --- ## 💡 技术细节:如何实现物理隔离 ### Step 1: 创建Schema ```sql -- 创建所有Schema CREATE SCHEMA platform_schema; CREATE SCHEMA aia_schema; CREATE SCHEMA asl_schema; CREATE SCHEMA pkb_schema; CREATE SCHEMA dc_schema; CREATE SCHEMA ssa_schema; CREATE SCHEMA st_schema; CREATE SCHEMA review_schema; CREATE SCHEMA admin_schema; ``` --- ### Step 2: 修改Prisma Schema **修改database配置?* ```prisma generator client { provider = "prisma-client-js" previewFeatures = ["multiSchema"] // 启用多Schema支持 ? } datasource db { provider = "postgresql" url = env("DATABASE_URL") schemas = [ "platform_schema", "aia_schema", "asl_schema", "pkb_schema", "dc_schema", "ssa_schema", "st_schema", "review_schema", "admin_schema" ] // 声明所有Schema ? } ``` **修改Model?* ```prisma // 平台? model User { id String @id @default(uuid()) email String @unique password String // ... @@map("users") @@schema("platform_schema") // 指定Schema ? } // AI问答模块 model AiaProject { id String @id @default(uuid()) userId String @map("user_id") name String user User @relation(fields: [userId], references: [id]) @@map("projects") // 不需要前缀 @@schema("aia_schema") // 指定Schema ? } // AI文献模块 model AslProject { id String @id @default(uuid()) userId String @map("user_id") name String user User @relation(fields: [userId], references: [id]) @@map("projects") @@schema("asl_schema") // 指定Schema ? } ``` --- ### Step 3: 生成迁移 ```bash # 生成迁移文件 npx prisma migrate dev --name schema-isolation --create-only # 查看生成的SQL # migrations/20251106_schema-isolation/migration.sql ``` **生成的SQL示例?* ```sql -- CreateSchema CREATE SCHEMA IF NOT EXISTS "platform_schema"; CREATE SCHEMA IF NOT EXISTS "aia_schema"; -- ... -- CreateTable CREATE TABLE "platform_schema"."users" ( "id" UUID NOT NULL DEFAULT gen_random_uuid(), "email" VARCHAR(255) NOT NULL, -- ... PRIMARY KEY ("id") ); CREATE TABLE "aia_schema"."projects" ( "id" UUID NOT NULL DEFAULT gen_random_uuid(), "user_id" UUID NOT NULL, "name" VARCHAR(255) NOT NULL, -- ... CONSTRAINT "fk_user" FOREIGN KEY ("user_id") REFERENCES "platform_schema"."users"("id") ON DELETE CASCADE, PRIMARY KEY ("id") ); ``` --- ### Step 4: 数据迁移 **编写迁移脚本?* ```sql -- migrate-data.sql -- 迁移用户? INSERT INTO platform_schema.users SELECT * FROM public.users; -- 迁移AIA模块 INSERT INTO aia_schema.projects SELECT * FROM public.aia_projects; INSERT INTO aia_schema.conversations SELECT * FROM public.aia_conversations; INSERT INTO aia_schema.messages SELECT * FROM public.aia_messages; -- 迁移PKB模块 INSERT INTO pkb_schema.knowledge_bases SELECT * FROM public.knowledge_bases; INSERT INTO pkb_schema.documents SELECT * FROM public.documents; -- ... 其他? ``` **执行迁移?* ```bash # 方式1:使用psql psql -U postgres -d ai_clinical_research -f migrate-data.sql # 方式2:使用Prisma npx prisma db execute --file migrate-data.sql ``` --- ### Step 5: 验证 ```typescript // test-schema-isolation.ts async function testSchemaIsolation() { // 测试跨Schema关联 const project = await prisma.aiaProject.findUnique({ where: { id: 'test-id' }, include: { user: true // 跨Schema关联(platform_schema.users? } }); console.log('跨Schema关联正常:', project); // 测试所有模? const stats = { users: await prisma.user.count(), aiaProjects: await prisma.aiaProject.count(), aslProjects: await prisma.aslProject.count(), knowledgeBases: await prisma.knowledgeBase.count(), }; console.log('数据统计:', stats); } ``` --- ## 📊 成本收益总结 ### 投入成本 | 项目 | 时间 | 风险 | |------|------|------| | 学习和准?| 1?| ?| | 开发环境改?| 1-2?| ?| | 测试验证 | 1-2?| ?| | 生产环境迁移 | 1?| ?| | **总计** | **5-6?* | **中等** | ### 预期收益 | 收益 | 价?| |------|------| | 模块独立部署 | ⭐⭐⭐⭐?| | 微服务拆?| ⭐⭐⭐⭐?| | 数据库级别权限控?| ⭐⭐⭐⭐ | | 按Schema备份恢复 | ⭐⭐⭐⭐ | | 避免未来大规模改?| ⭐⭐⭐⭐?| | **总价?* | **极高** | --- ## 🎯 最终建议总结 ### 我的建议:立即做物理隔离(方案A? **核心理由?* 1. ?**现在是成本最低的时间窗口**(数据量小,改造快? 2. ?**避免技术债累?*(未来改造成本成倍增长) 3. ?**?个模块打下坚实基础**(模块独立部署、微服务拆分? 4. ?**支持未来商业模式**(模块化销售、私有化部署? **投入?* 1? **收益?* 避免未来3-5倍的改造成? **投入产出比:** 极高 --- **如果您决定采纳方案A,我可以立即帮您?* 1. 设计详细的Schema结构 2. 编写Prisma Schema修改方案 3. 编写数据迁移脚本 4. 制定详细的实施计? 5. 准备测试用例和验收标? **您觉得呢?* 😊