多租户架构:从租户到世界的 5 级隔离模型
TL;DR
多租户架构:从租户到世界的 5 级隔离模型
“系列:S2 架构全景 · 第 5 篇 | 难度:中级 | 阅读时间:18 分钟
TL;DR
- coomia-dip 采用 Tenant → Org → Space → Project → World 的 5 级隔离模型,每一级都有明确的资源边界和权限作用域。相比扁平的"租户-用户"两级模型,5 级隔离更好地映射了企业组织结构的现实复杂度。
- 数据隔离在 Project 级通过 Doris Database-per-Project 实现物理隔离,World 级通过 Nessie Branch 实现逻辑隔离。两者结合确保了跨租户的绝对隔离和同项目内的灵活版本管理。
- 自动供应(Auto-Provisioning)机制让新层级创建时自动初始化所有必要资源——Doris 数据库、Nessie 分支、Kafka 主题、MinIO 桶——从"申请工单等 3 天"变为"API 调用等 3 秒"。
#1. 引言:为什么需要 5 级
大多数 SaaS 平台的多租户模型只有两级:租户(Tenant) 和 用户(User)。这在简单场景下足够了,但当你的客户是大型企业集团时,问题就出现了。
考虑一个真实场景:
华能集团(租户)
├── 华能国际(子公司 → Org)
│ ├── 华东分公司(业务单元 → Space)
│ │ ├── 智能运维项目(项目 → Project)
│ │ │ ├── 生产环境(世界 → World)
│ │ │ └── 测试环境(世界 → World)
│ │ └── 碳排放监控项目(项目 → Project)
│ └── 华南分公司(业务单元 → Space)
├── 华能新能源(子公司 → Org)
│ └── 风电事业部(业务单元 → Space)
│ └── 风机健康监测项目(项目 → Project)
└── 华能核电(子公司 → Org)
如果用两级模型硬套:
- 华东分公司的运维工程师能看到华南分公司的数据吗?不应该。
- 智能运维项目的测试环境数据会影响生产环境吗?绝对不能。
- 华能国际的管理者能看到华能新能源的风电数据吗?取决于集团策略。
两级模型无法表达这些需求。 你要么写大量的自定义权限逻辑,要么接受安全漏洞。coomia-dip 的 5 级隔离模型正是为了解决这个问题。
#2. 5 级隔离模型详解
#2.1 层级定义
Level 1: Tenant (租户)
│ 最高隔离边界。不同租户之间完全不可见。
│ 典型映射:集团公司、独立企业
│
├─ Level 2: Org (组织)
│ │ 法律实体或独立核算单位。同一租户下的不同组织
│ │ 默认互相不可见,需要显式授权。
│ │ 典型映射:子公司、事业部
│ │
│ ├─ Level 3: Space (空间)
│ │ │ 业务协作单元。同一组织下的不同团队或部门。
│ │ │ 默认互相可见(同组织),权限可细化。
│ │ │ 典型映射:部门、区域分公司、业务线
│ │ │
│ │ ├─ Level 4: Project (项目)
│ │ │ │ 独立的数据域。拥有独立的 Doris Database。
│ │ │ │ 典型映射:业务项目、应用系统
│ │ │ │
│ │ │ ├─ Level 5: World (世界)
│ │ │ │ 项目的版本化实例。Nessie Branch 隔离。
│ │ │ │ 典型映射:生产环境、测试环境、模拟沙箱
#2.2 每一级的隔离机制
| 级别 | 隔离类型 | 技术实现 | 跨级可见性 |
|---|---|---|---|
| Tenant | 绝对隔离 | 独立的身份域 + 独立的加密密钥 | 永远不可跨租户 |
| Org | 强隔离 | 独立的权限树 + 独立的配额 | 需租户管理员显式授权 |
| Space | 逻辑隔离 | RBAC 角色域 + 资源标签 | 同组织内默认可发现 |
| Project | 数据隔离 | Doris Database-per-Project | 需 Space 级授权 |
| World | 版本隔离 | Nessie Branch-per-World | 同 Project 内可切换 |
#2.3 WorldContext:贯穿 5 级的上下文对象
在 coomia-dip 中,每一个 gRPC 调用都必须携带 WorldContext,它是 5 级隔离模型的运行时表达:
message WorldContext {
string tenant_id = 1; // Level 1: 租户
string org_id = 2; // Level 2: 组织
string space_id = 3; // Level 3: 空间
string project_id = 4; // Level 4: 项目
string world_id = 5; // Level 5: 世界
// 认证信息
string principal_id = 6; // 当前用户
repeated string roles = 7; // 用户角色列表
map<string, string> claims = 8; // 额外声明 (ABAC 用)
}
这个对象从 API Gateway 开始构建,在整个请求链路中传递,确保每个组件都知道当前操作在哪个隔离级别中执行。
#3. 数据隔离的技术实现
#3.1 Level 4 隔离:Doris Database-per-Project
每个 Project 拥有独立的 Doris Database。这不是在同一个 Database 中用 project_id 列来区分数据——那是逻辑隔离;而是真正的物理隔离。
Doris 实例 (共享)
├── database: proj_huaneng_ops_001
│ ├── entity_common (对象实例)
│ ├── entity_edge (关系实例)
│ ├── entity_event (事件记录)
│ └── entity_metric (指标数据)
│
├── database: proj_huaneng_carbon_002
│ ├── entity_common
│ ├── entity_edge
│ ├── entity_event
│ └── entity_metric
│
├── database: proj_wind_health_003
│ ├── entity_common
│ ├── entity_edge
│ ├── entity_event
│ └── entity_metric
为什么选择 Database-per-Project 而不是 Database-per-Tenant?
方案对比:
Database-per-Tenant:
├── 优点: 更简单的管理,更少的 Database 数量
├── 缺点: 同一租户下不同项目共享 Database
│ → 一个项目的大查询影响其他项目
│ → DDL 变更需要协调同租户的所有项目
│ → 无法为单个项目独立配置资源配额
└── 结论: ❌ 隔离粒度不够
Database-per-Project:
├── 优点: 每个项目独立的资源配额和性能隔离
│ → 大查询不影响其他项目
│ → DDL 变更仅影响当前项目
│ → 可以独立扩缩容
├── 缺点: Database 数量更多 (但 Doris 支持数千个 Database)
└── 结论: ✅ 最佳平衡点
Database-per-World:
├── 优点: 最极致的隔离
├── 缺点: Database 数量爆炸 (每个项目可能有 10+ 个 World)
│ → 资源浪费严重
│ → 跨 World 查询几乎不可能
└── 结论: ❌ 过度隔离
#3.2 Level 5 隔离:Nessie Branch-per-World
World 级别的隔离通过 Nessie 分支实现。Nessie 是 Git-like 的数据湖目录管理器,与 Iceberg 表格式配合工作。
Nessie Repository (per Project)
├── Branch: world_production ← 生产环境
│ └── Iceberg Tables:
│ ├── entity_common (指向 Iceberg 快照 S-2024-001)
│ ├── entity_edge (指向 Iceberg 快照 S-2024-002)
│ └── entity_event (指向 Iceberg 快照 S-2024-003)
│
├── Branch: world_staging ← 预发布环境
│ └── Iceberg Tables:
│ ├── entity_common (指向 Iceberg 快照 S-2024-004)
│ ├── entity_edge (从 production 继承)
│ └── entity_event (从 production 继承)
│
├── Branch: world_sandbox_alice ← Alice 的沙箱
│ └── Iceberg Tables: (从 production 分叉)
│ └── ...
│
└── Branch: world_whatif_q3 ← Q3 计划模拟
└── Iceberg Tables: (从 production 分叉)
└── ...
Nessie 分支的关键特性:
- O(1) 创建:创建一个新 World 只需创建一个 Nessie 分支,这是一个元数据操作,耗时 < 100ms。不需要复制任何数据。
- Copy-on-Write:新 World 共享父 World 的所有 Iceberg 数据文件,只有在新 World 中发生写入时才产生新的数据文件。
- 可合并:沙箱中的修改可以通过 Nessie Merge 操作合并回生产环境,类似 Git 的 merge。
- 时间旅行:每个 World 保留完整的版本历史,可以回溯到任意时间点的数据状态。
#3.3 Doris + Nessie 的协同工作
读取操作的路由:
请求到达 → 提取 WorldContext
→ project_id → 确定 Doris Database
→ world_id → 确定 Nessie Branch
Case 1: 实时数据查询 (热数据)
→ 查询 Doris Database (project_id)
→ SQL 中注入 world_id 过滤条件
→ 返回结果
Case 2: 历史数据查询 / 时间旅行
→ 查询 Nessie Branch (world_id)
→ 获取指定时间点的 Iceberg 快照
→ 通过 Doris 的 Iceberg Catalog 查询
→ 返回结果
Case 3: 跨 World 对比
→ 同时查询两个 Nessie Branch
→ 比较两个 Iceberg 快照的差异
→ 返回 diff 结果
#4. 计算隔离
#4.1 资源配额模型
计算隔离确保一个租户/项目的工作负载不会影响其他租户/项目。coomia-dip 在多个级别实施资源配额:
Tenant Level Quota:
├── max_orgs: 50
├── max_total_storage: 10TB
├── max_concurrent_queries: 1000
└── max_api_calls_per_minute: 10000
Org Level Quota:
├── max_spaces: 20
├── max_storage: 2TB (不超过 Tenant 上限)
└── max_concurrent_queries: 200
Space Level Quota:
├── max_projects: 10
└── max_storage: 500GB
Project Level Quota:
├── max_worlds: 20
├── doris_database_size_limit: 100GB
├── max_concurrent_queries: 50
├── max_object_count: 10,000,000
└── max_derived_property_computations_per_minute: 1000
World Level Quota:
├── max_nessie_branch_size: 50GB (Iceberg 数据)
├── max_concurrent_users: 100
└── max_event_rate: 500/second
#4.2 查询资源隔离
Doris 的 Workload Group 功能用于实现查询级别的资源隔离:
-- 为每个 Project 创建 Workload Group
CREATE WORKLOAD GROUP proj_huaneng_ops_001
PROPERTIES (
'cpu_share' = '10',
'memory_limit' = '30%',
'max_concurrency' = '50',
'max_queue_size' = '100',
'queue_timeout' = '5000'
);
-- 查询时自动绑定 Workload Group
SET workload_group = 'proj_huaneng_ops_001';
SELECT ... FROM entity_common WHERE ...;
这确保了即使某个项目的用户执行了一个非常耗资源的查询,也不会影响其他项目的查询性能。
#4.3 推理计算隔离
Reasoning & Decision Layer(推理与决策)的计算任务(规则引擎、派生属性计算、AI 推理)通过 Kubernetes 命名空间和资源限制实现隔离:
# 每个 Tenant 一个 Kubernetes Namespace
apiVersion: v1
kind: Namespace
metadata:
name: tenant-huaneng
labels:
coomia-dip/tenant-id: "huaneng"
---
# ResourceQuota 限制租户的计算资源
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: tenant-huaneng
spec:
hard:
requests.cpu: "32"
requests.memory: "64Gi"
limits.cpu: "64"
limits.memory: "128Gi"
pods: "100"
#5. 权限继承模型
#5.1 继承方向:自上而下
权限在 5 级模型中自上而下继承,高层级的权限自动传递到下层级:
Tenant Admin (租户管理员)
│ 拥有该租户下所有资源的完全管理权限
│
├── Org Admin (组织管理员)
│ │ 拥有该组织下所有 Space、Project、World 的管理权限
│ │ 继承自 Tenant Admin 的授权
│ │
│ ├── Space Admin (空间管理员)
│ │ │ 拥有该空间下所有 Project、World 的管理权限
│ │ │
│ │ ├── Project Member (项目成员)
│ │ │ │ 拥有该项目下所有 World 的操作权限
│ │ │ │
│ │ │ └── World Viewer (世界查看者)
│ │ │ 仅能查看特定 World 的数据
#5.2 权限覆盖规则
规则 1: 高层级授权 → 低层级自动继承
Org Admin 对 OrgA 授权 → 自动拥有 OrgA 下所有 Space 的权限
规则 2: 低层级可以收紧但不能放大
Space Admin 不能授予超过 Org Admin 给予的权限
规则 3: 显式拒绝优先于继承允许
即使 Org Admin 允许访问,Space 级别的显式 DENY 仍然生效
规则 4: 跨级授权需要管理员
将 Org A 的用户添加到 Org B 的 Space 中,需要 Tenant Admin
优先级链:
显式 DENY > 显式 ALLOW > 继承 DENY > 继承 ALLOW > 默认 DENY
#5.3 RBAC + ABAC 混合模型
RBAC 处理"角色能做什么",ABAC 处理"在什么条件下能做什么":
RBAC 角色定义 (每个 Space 可自定义):
Role: DataEngineer
Permissions:
- ObjectType.* : READ
- ObjectType.create : ALLOW (限 data-source 类型)
- Pipeline.* : READ, WRITE, EXECUTE
- World.switch : ALLOW
Role: DataAnalyst
Permissions:
- ObjectType.* : READ
- DerivedProperty.* : READ, CREATE
- Search.* : READ
- Export.* : ALLOW (限 1000 行)
Role: BusinessUser
Permissions:
- Dashboard.* : READ
- Action.execute : ALLOW (限 approved 类型)
- World.switch : DENY (只能看到生产 World)
ABAC 策略示例:
Policy: "sensitive-data-time-restriction"
Condition:
resource.classification == "CONFIDENTIAL"
AND time.hour < 8 OR time.hour > 20
Effect: DENY
Reason: "机密数据仅在工作时间可访问"
Policy: "geo-restriction"
Condition:
resource.region != principal.department_region
Effect: DENY
Reason: "只能访问本区域的数据"
Policy: "data-volume-limit"
Condition:
request.type == "EXPORT"
AND request.row_count > 10000
AND principal.role != "DataEngineer"
Effect: DENY
Reason: "非工程师角色导出不超过 10000 行"
#6. 自动供应(Auto-Provisioning)
#6.1 传统流程的痛点
传统企业平台创建一个新项目环境的流程通常是这样的:
Day 1: 提交工单 → "请创建新的数据库实例"
Day 2: DBA 审批 → 排队等待
Day 3: DBA 创建数据库 → 配置用户权限
Day 4: 提交工单 → "请创建新的 Kafka Topic"
Day 5: 消息中间件管理员审批 → 创建 Topic
Day 6: 提交工单 → "请创建 MinIO Bucket"
Day 7: 存储管理员审批 → 创建 Bucket
Day 8: 开始开发 → 发现缺少 Nessie 配置
Day 9: ...
总耗时: 1-2 周
涉及人员: 3-5 个不同团队
失败概率: 约 30% (某个环节遗漏或配置错误)
#6.2 Auto-Provisioning 架构
coomia-dip 通过 Auto-Provisioning 机制将上述流程自动化:
创建新 Project:
API: POST /api/v1/admin/projects
ProjectProvisioner 自动执行:
├── Step 1: 验证配额 (1ms)
│ ├── 检查 Space 下 Project 数量是否超限
│ ├── 检查存储配额是否充足
│ └── 检查计算资源是否可分配
│
├── Step 2: 创建 Doris Database (200ms)
│ ├── CREATE DATABASE proj_{tenant}_{name}_{id}
│ ├── CREATE TABLE entity_common (...)
│ ├── CREATE TABLE entity_edge (...)
│ ├── CREATE TABLE entity_event (...)
│ ├── CREATE WORKLOAD GROUP proj_{id} (...)
│ └── GRANT SELECT, INSERT, UPDATE ON proj_{id}.* TO svc_account
│
├── Step 3: 创建 Nessie Repository (100ms)
│ ├── 创建 Nessie Namespace
│ ├── 创建 main Branch (默认 World)
│ └── 初始化 Iceberg 表元数据
│
├── Step 4: 创建 Kafka Topics (300ms)
│ ├── proj_{id}.data-change (数据变更事件, 12 partitions)
│ ├── proj_{id}.audit.access (审计日志, 6 partitions)
│ ├── proj_{id}.audit.mutation (审计日志, 6 partitions)
│ ├── proj_{id}.subscription (订阅路由, 6 partitions)
│ └── proj_{id}.derived-property (派生属性级联, 6 partitions)
│
├── Step 5: 创建 MinIO Bucket (50ms)
│ ├── 创建 bucket: proj-{id}
│ ├── 设置生命周期策略
│ └── 配置访问策略
│
├── Step 6: 创建 Redis 命名空间 (10ms)
│ ├── 配置 key 前缀: proj:{id}:
│ └── 设置内存配额
│
├── Step 7: 初始化元数据 (50ms)
│ ├── 在 PostgreSQL 注册 Project 元数据
│ ├── 创建默认角色和权限
│ └── 记录供应审计日志
│
└── 完成! 总耗时: ~800ms
从"提交工单等 1 周"到"API 调用等 1 秒"
#6.3 World 的快速创建
创建新 World 比创建 Project 更快,因为 World 共享 Project 的 Doris Database:
创建新 World:
WorldProvisioner 自动执行:
├── Step 1: 验证配额 (1ms)
│ └── 检查 Project 下 World 数量是否超限
│
├── Step 2: 创建 Nessie Branch (50ms)
│ ├── 从源 Branch (通常是 production) 分叉
│ └── 这是纯元数据操作,不复制数据
│
├── Step 3: 初始化 World 上下文 (20ms)
│ ├── 注册 World 元数据
│ └── 创建 world_id 的权限记录
│
└── 完成! 总耗时: ~80ms
创建一个新的模拟沙箱: 80ms
沙箱拥有与生产环境完全相同的数据 (Copy-on-Write)
沙箱中的修改不影响生产环境
#6.4 自动清理(Auto-Deprovisioning)
当删除 Project 或 World 时,Auto-Deprovisioning 自动清理所有关联资源:
删除 Project:
ProjectDeprovisioner 自动执行:
├── Step 1: 验证无活跃连接 (10ms)
├── Step 2: 创建最终快照到 MinIO (异步, 不阻塞)
├── Step 3: 删除 Kafka Topics (200ms)
├── Step 4: 删除 Nessie Repository (100ms)
├── Step 5: DROP DATABASE proj_{id} (500ms)
├── Step 6: 清理 Redis 命名空间 (10ms)
├── Step 7: 标记 MinIO Bucket 过期 (不立即删除, 30 天后自动清理)
├── Step 8: 更新 PostgreSQL 元数据 (10ms)
└── Step 9: 记录清理审计日志 (5ms)
数据保留策略:
- Doris 数据: 立即删除
- MinIO 数据: 保留 30 天 (可恢复)
- 审计日志: 根据主题保留期保留
- PostgreSQL 元数据: 软删除, 保留 90 天
#7. 跨级查询与数据共享
#7.1 跨 World 查询
同一 Project 下的不同 World 之间可以进行对比查询,这是"模拟沙箱"功能的基础:
场景: 对比生产环境和模拟沙箱的数据差异
API: POST /api/v1/ontology/worlds/compare
{
"source_world": "world_production",
"target_world": "world_sandbox_q3",
"object_type": "InventoryItem",
"compare_properties": ["quantity", "reorder_point"]
}
内部实现:
1. 获取 production 的 Nessie Branch → Iceberg 快照 A
2. 获取 sandbox_q3 的 Nessie Branch → Iceberg 快照 B
3. Iceberg 提供快照级 diff:
- 新增的数据文件
- 删除的数据文件
- 修改的行 (通过 position-delete 文件)
4. 返回差异结果
#7.2 跨 Project 数据共享
不同 Project 之间默认不可直接查询,但可以通过 DataLink 机制共享特定的 ObjectType:
DataLink 定义:
{
"link_name": "shared_equipment_catalog",
"source_project": "proj_asset_management",
"source_object_type": "Equipment",
"target_project": "proj_maintenance",
"exposed_properties": ["name", "location", "model", "install_date"],
"hidden_properties": ["purchase_price", "depreciation"],
"access_mode": "READ_ONLY",
"sync_mode": "REAL_TIME" // 通过 Kafka CDC 实时同步
}
DataLink 确保:
- 目标 Project 只能看到显式暴露的属性
- 数据以只读方式提供
- 访问仍然通过 OntologyRuntimeService,受权限检查和审计约束
- 数据血缘自动记录跨 Project 的依赖关系
#7.3 跨 Tenant 隔离的绝对性
跨 Tenant 的数据共享是绝对禁止的。没有 DataLink,没有管理员后门,没有任何机制可以让 Tenant A 看到 Tenant B 的数据。
隔离保证:
1. 身份隔离: Tenant A 的用户 Token 中的 tenant_id
与 Tenant B 的资源 tenant_id 永远不会匹配
2. 网络隔离: 不同 Tenant 的 gRPC 请求携带不同的 TLS 证书
3. 存储隔离: 不同 Tenant 的 Doris Database 名称包含 tenant_id
SQL 注入也无法跨 Database 查询 (Doris 权限控制)
4. 缓存隔离: Redis 的 key 前缀包含 tenant_id
即使缓存穿透也不会返回其他租户的数据
5. 审计隔离: 审计日志中的 tenant_id 不可伪造
由 API Gateway 从认证 Token 中提取并注入
#8. 实际部署拓扑
#8.1 小规模部署(< 10 租户)
┌─────────────────────────────────────────┐
│ 共享基础设施 │
│ │
│ ┌─────────┐ ┌──────┐ ┌──────────┐ │
│ │ Doris │ │Kafka │ │PostgreSQL│ │
│ │ (共享) │ │(共享) │ │ (共享) │ │
│ │ │ │ │ │ │ │
│ │ DB: t1p1 │ │ │ │ │ │
│ │ DB: t1p2 │ │ │ │ │ │
│ │ DB: t2p1 │ │ │ │ │ │
│ └─────────┘ └──────┘ └──────────┘ │
│ │
│ ┌──────┐ ┌──────┐ ┌──────────────┐ │
│ │Redis │ │MinIO │ │Nessie+Iceberg│ │
│ │(共享) │ │(共享) │ │ (共享) │ │
│ └──────┘ └──────┘ └──────────────┘ │
│ │
│ 隔离方式: Database / Branch / Namespace │
└─────────────────────────────────────────┘
#8.2 大规模部署(100+ 租户)
┌──────────────────────────────────────────────┐
│ 租户组 A (金融行业客户) │
│ │
│ ┌─────────┐ ┌──────┐ ┌──────────────┐ │
│ │ Doris │ │Kafka │ │Nessie+Iceberg│ │
│ │ Cluster │ │Cluster│ │ Cluster │ │
│ │ (专属) │ │ (专属) │ │ (专属) │ │
│ └─────────┘ └──────┘ └──────────────┘ │
│ │
│ 特点: 独立集群, 物理隔离, 合规认证 │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ 租户组 B (制造业客户, 共享资源) │
│ │
│ ┌─────────┐ ┌──────┐ ┌──────────────┐ │
│ │ Doris │ │Kafka │ │Nessie+Iceberg│ │
│ │ Cluster │ │Cluster│ │ Cluster │ │
│ │ (共享) │ │ (共享) │ │ (共享) │ │
│ └─────────┘ └──────┘ └──────────────┘ │
│ │
│ 特点: 共享集群, 逻辑隔离, 成本优化 │
└──────────────────────────────────────────────┘
#8.3 混合部署策略
决策矩阵:
┌─────────────────┬─────────────┬─────────────┐
│ 客户类型 │ 隔离级别 │ 部署方式 │
├─────────────────┼─────────────┼─────────────┤
│ 金融/医疗 │ 物理隔离 │ 专属集群 │
│ 政府 │ 物理隔离 │ 专属集群 │
│ 大型制造业 │ 混合隔离 │ 共享集群+专属DB│
│ 中小企业 │ 逻辑隔离 │ 共享集群 │
│ 试用/POC │ 逻辑隔离 │ 共享集群 │
└─────────────────┴─────────────┴─────────────┘
#9. 监控与告警
#9.1 多租户监控维度
Level 1 监控 (Tenant):
- API 调用量 / 分钟
- 总存储使用量
- 活跃用户数
- 错误率
Level 4 监控 (Project):
- Doris Database 大小
- 查询 QPS 和延迟 P99
- Workload Group 资源使用率
- Kafka Topic 消费延迟
Level 5 监控 (World):
- Nessie Branch 大小
- 活跃连接数
- 事件发布频率
- 派生属性计算队列长度
#9.2 "吵闹邻居"检测
Noisy Neighbor Detection:
触发条件:
1. 某 Project 的 Doris 查询延迟 > P95 的 2 倍
2. 某 Project 的 Kafka 生产速率 > 配额的 80%
3. 某 Project 的 CPU 使用率 > Workload Group 限制的 90%
自动响应:
1. 告警通知 → 项目负责人 + 平台运维
2. 自动限流 → 降低该 Project 的并发限制
3. 查询日志 → 记录导致高负载的查询语句
4. 建议优化 → 自动分析慢查询并给出索引建议
#10. 与 Palantir Foundry 的多租户对比
| 维度 | Palantir Foundry | coomia-dip |
|---|---|---|
| 隔离层级 | 3 级 (Enrollment → Org → Project) | 5 级 (+Space +World) |
| 版本化 | Dataset 级 Transaction | World 级 Nessie Branch |
| 数据隔离 | 共享 HDFS + ACL | Database-per-Project |
| 计算隔离 | Spark 资源池 | Doris Workload Group + K8s Namespace |
| 模拟沙箱 | Branch (AIP 功能) | World (原生支持) |
| 自动供应 | Enrollment 流程 (人工审批) | API 驱动 (< 1 秒) |
coomia-dip 的 5 级模型比 Foundry 多了 Space 和 World 两个层级。Space 更好地映射了企业内部的部门结构,World 则提供了原生的"模拟沙箱"能力——这在 Foundry 中需要 AIP(AI Platform)的高级功能支持。
#11. 常见问题与最佳实践
#11.1 何时创建新的 Org vs Space
创建新 Org:
- 独立法律实体(子公司)
- 需要独立计费
- 需要完全独立的管理员
- 数据默认不互通
创建新 Space:
- 同一法律实体内的部门
- 共享计费
- 可能需要跨部门协作
- 数据在组织内默认可发现
#11.2 何时创建新的 World
必须创建新 World:
- 测试/预发布环境 → 避免影响生产数据
- What-if 模拟 → 修改数据不影响真实环境
- 数据迁移验证 → 在沙箱中测试 Schema 变更
- 培训环境 → 为新用户提供安全的练习空间
不需要创建新 World:
- 简单的权限隔离 → 用 RBAC/ABAC 即可
- 不同的视图/仪表板 → 用 Dashboard 配置即可
- 历史数据查看 → 用时间旅行(Nessie 快照)即可
#Key Takeaways
-
5 级隔离模型映射了企业组织的真实复杂度。 Tenant → Org → Space → Project → World 的层级结构,比扁平的"租户-用户"模型多了 3 个关键维度:Org 映射法律实体、Space 映射业务单元、World 映射版本化环境。每增加一级隔离,权限管理和资源控制的粒度就更细一层。
-
数据隔离的"双保险"设计是关键。 Doris Database-per-Project 提供物理级别的数据隔离,Nessie Branch-per-World 提供逻辑级别的版本隔离。前者确保不同项目之间没有任何数据泄露的可能,后者让同一项目内的多版本管理变得轻量且零成本(O(1) 分支创建,Copy-on-Write 数据共享)。
-
Auto-Provisioning 将环境创建从"周"级降到"秒"级。 传统企业需要 1-2 周和 3-5 个团队的协调才能创建一个新项目环境。coomia-dip 通过自动化供应,在 < 1 秒内完成 Doris Database、Nessie Repository、Kafka Topics、MinIO Bucket 的创建。这不只是效率提升——它改变了团队使用环境的方式:从"珍惜地使用稀缺环境"变为"随时创建用完即弃的沙箱"。
“下一篇预告: [S2-06] 事件驱动架构:Kafka 在平台中的 7 种角色——深入理解 Kafka 如何承担 CDC、审计、订阅路由、管道触发、派生属性级联、推理触发和跨 Layer 协调共 7 种不同的角色。
Tags: #multi-tenant #isolation #world #nessie #doris #auto-provisioning #rbac #abac #coomia-dip #智策平台