第五章 多租户与多应用设计
多租户设计的目标,不是简单给每张表加一个 tenant_id。真正的目标是:同一套平台能服务多个组织、多个产品、多个环境,同时保证数据隔离、配置独立、权限清晰和成本可控。
产品矩阵平台中,多租户和多应用是两个不同维度:
| 维度 | 含义 | 示例 |
|---|---|---|
| Tenant | 使用平台的主体 | 企业、品牌、客户、开发者 |
| App | 租户下的具体产品 | 小程序、Web 站、管理后台 |
| Namespace | 逻辑命名空间 | dev、prod、活动专区 |
5.1 多租户隔离策略
常见隔离方式有三种:
| 模式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 共享库共享表 | 成本低、运维简单 | 隔离弱、查询必须严谨 | 早期 SaaS、长尾租户 |
| 共享库独立表 | 部分隔离、迁移灵活 | 表数量膨胀 | 中型租户、半独立业务 |
| 独立库 | 隔离强、合规友好 | 运维成本高 | 大客户、金融医疗 |
平台可以采用混合策略:默认共享库共享表,大客户升级为独立库,审计、账单等平台公共数据仍保留在中心库。
5.2 Tenant、App 与 Namespace 模型
建议模型关系:
erDiagram
TENANT ||--o{ APP : owns
APP ||--o{ APP_ENV : has
APP ||--o{ APP_CONFIG : configures
TENANT ||--o{ MEMBERSHIP : has
USER ||--o{ MEMBERSHIP : joins
核心字段:
| 表 | 关键字段 |
|---|---|
tenants | id、name、plan、status |
apps | id、tenant_id、type、name、status |
app_envs | app_id、namespace、domain、secret |
memberships | tenant_id、user_id、role |
AppID 和 AppSecret 不应直接等同于数据库主键。它们是对外凭证,应可轮换、可禁用、可审计。
5.3 租户上下文注入
租户上下文应在请求入口统一解析。
解析来源可以按优先级排列:
- 自定义域名,如
acme.example.com; - Header,如
X-App-ID; - JWT Claims,如
tenant_id、app_id; - 路径前缀,如
/t/{tenant_slug}; - 开放平台签名参数。
解析完成后写入 context.Context,后续 Service、Repository、日志、审计都从上下文读取。
5.4 数据表 Schema 设计
共享表模式下,业务表至少要有:
| 字段 | 说明 |
|---|---|
tenant_id | 租户隔离 |
app_id | 应用隔离或来源 |
namespace | 环境或空间 |
created_by | 创建人 |
updated_by | 更新人 |
deleted_at | 软删除 |
索引不能只按业务字段建。例如内容表常见查询是“某租户某 App 下按状态分页”,索引应考虑:
CREATE INDEX idx_contents_tenant_app_status_time
ON contents (tenant_id, app_id, status, published_at);
缺少 tenant_id 前缀的索引,在多租户数据量变大后会非常昂贵。
5.5 应用注册与生命周期管理
App 不是一条配置记录,而是有生命周期的对象。
created -> configured -> verified -> active -> suspended -> archived
| 状态 | 含义 |
|---|---|
| created | 已创建但未配置凭证 |
| configured | 已配置域名、回调、密钥 |
| verified | 域名或平台审核通过 |
| active | 可正常访问 |
| suspended | 欠费、违规或管理员暂停 |
| archived | 归档,不再对外服务 |
生命周期变化必须写审计日志,尤其是暂停、恢复、密钥轮换和删除。
5.6 App 配置分层
配置继承顺序建议为:
platform default
-> tenant default
-> app config
-> namespace config
-> runtime override
读取配置时要返回“最终值”和“来源”。运维排障时,知道某个开关为什么是 true,往往比值本身更重要。
5.7 SaaS 层级设计
平台可支持三种 SaaS 形态:
| 形态 | 说明 | 示例 |
|---|---|---|
| 单体 SaaS | 一个租户使用标准产品 | 中小企业订阅 CRM |
| 子租户 SaaS | 租户下继续管理客户 | 代理商管理多个门店 |
| 白标 SaaS | 租户拥有品牌和域名 | 给行业客户输出独立品牌 |
白标 SaaS 需要更强的主题、域名、邮件发件人、支付主体和备案支持,不能只换 Logo。
5.8 API 网关级多租户路由
网关应在转发前完成租户识别和基础校验。
| 路由方式 | 例子 | 优点 |
|---|---|---|
| 独立域名 | api.acme.com | 品牌独立、隔离清晰 |
| 子域名 | acme.platform.com | 管理方便 |
| Header | X-App-ID | SDK 和服务调用友好 |
| 路径 | /tenant/acme/api | 开发简单 |
生产环境优先推荐域名或 Header,路径模式适合开发、测试和内部工具。
5.9 常见风险
| 风险 | 后果 | 防护 |
|---|---|---|
查询漏加 tenant_id | 数据串租户 | ORM Scope、测试、审计 |
| 后台超级权限滥用 | 合规事故 | 审批、双人复核、审计 |
| 配置覆盖不透明 | 线上行为不可解释 | 配置来源追踪 |
| 租户删除过于直接 | 数据不可恢复 | 冷归档、延迟删除 |
| 密钥不可轮换 | 泄露后无法止血 | 多密钥版本 |
多租户不是一个功能点,而是贯穿身份、数据、配置、日志、计费和运维的系统约束。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。