multitenant
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMultitenant — Quick Reference
多租户架构——快速参考
Servir plusieurs clients (tenants) sur la même base de code avec isolation stricte et un coût d'infra contrôlé.
在同一代码库中为多个客户(租户)提供服务,同时保证严格隔离并控制基础设施成本。
Trois tiers d'isolation
三层隔离方案
| Tier | Isolation | Coût | Cas d'usage |
|---|---|---|---|
| Tier 1 — Shared schema | colonne | Faible | Startups, free / petits clients |
| Tier 2 — Dedicated schema | un schéma PostgreSQL par tenant | Moyen | SMB, clients exigeants |
| Tier 3 — Dedicated DB | une base entière par tenant | Élevé | Enterprise, compliance stricte (HDS, FedRAMP) |
Règle de migration : commencer Tier 1, migrer un client en Tier 2/3 quand il représente > 20 % du revenu OU exige un SLA spécifique.
| 层级 | 隔离程度 | 成本 | 使用场景 |
|---|---|---|---|
| Tier 1 — 共享Schema | 所有表均包含 | 低 | 初创公司、免费/小型客户 |
| Tier 2 — 专属Schema | 每个租户对应一个PostgreSQL Schema | 中 | 中小企业、要求较高的客户 |
| Tier 3 — 专属数据库 | 每个租户对应一个完整数据库 | 高 | 企业客户、严格合规要求(HDS、FedRAMP) |
迁移规则:从Tier 1开始,当某一客户贡献超过20%的收入或需要特定SLA时,将其迁移至Tier 2/3。
Cinq invariants non-négociables
五项不可妥协的原则
- propagé à chaque requête (AsyncLocalStorage / SecurityContext / middleware).
tenant_id - PostgreSQL Row-Level Security (RLS) activé sur TOUTES les tables. Filet de sécurité contre un oubli applicatif.
- Tests d'isolation obligatoires. Tenant A ne doit jamais lire/écrire les données de B — y compris via tri, requête nuée, agrégat.
- Audit trail isolé par tenant. Pas de log multi-tenant cross-référencé sans permission explicite.
- Field-level encryption sur PII / secrets sensibles (Halite PHP, Eloquent Casts, libsodium).
- 需在每个请求中传递(通过AsyncLocalStorage / SecurityContext / 中间件)。
tenant_id - 所有表必须启用PostgreSQL行级安全(RLS)。作为应用层遗漏时的安全兜底。
- 必须进行隔离测试。租户A绝不能读取/写入租户B的数据——包括通过排序、批量查询、聚合操作。
- 按租户隔离审计日志。未经明确许可,不得交叉引用多租户日志。
- 对PII/敏感机密数据进行字段级加密(可使用Halite PHP、Eloquent Casts、libsodium)。
Pattern minimal — Shared schema + RLS
最简实现方案——共享Schema + RLS
sql
ALTER TABLE invoices ADD COLUMN tenant_id UUID NOT NULL;
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON invoices
USING (tenant_id = current_setting('app.tenant_id')::uuid);php
// Symfony — middleware qui set la variable session pour RLS
$conn->executeStatement(
'SET LOCAL app.tenant_id = :tid',
['tid' => $tenantId]
);sql
ALTER TABLE invoices ADD COLUMN tenant_id UUID NOT NULL;
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON invoices
USING (tenant_id = current_setting('app.tenant_id')::uuid);php
// Symfony — 设置RLS会话变量的中间件
$conn->executeStatement(
'SET LOCAL app.tenant_id = :tid',
['tid' => $tenantId]
);Anti-patterns critiques
关键反模式
- ❌ Oublier le filtre dans une requête raw → fuite cross-tenant.
tenant_id - ❌ Cache Redis sans préfixe tenant → données de A retournées à B.
- ❌ Job worker async qui perd le → impossible de retrouver le contexte.
tenant_id - ❌ Signed URLs / tokens sans dans le payload → utilisable cross-tenant.
tenant_id - ❌ Field encryption avec une clé unique partagée → compromission = exposition totale (préférer keys per tenant).
- ❌ 在原生SQL查询中遗漏过滤条件 → 跨租户数据泄露。
tenant_id - ❌ Redis缓存未添加租户前缀 → 租户A的数据返回给租户B。
- ❌ 异步任务Worker丢失→ 无法恢复上下文。
tenant_id - ❌ 签名URL/令牌的载荷中未包含→ 可跨租户使用。
tenant_id - ❌ 使用单一共享密钥进行字段加密 → 密钥泄露导致全部数据暴露(建议按租户分配密钥)。
RBAC / ABAC
RBAC / ABAC权限控制
- RBAC : rôles globaux (,
admin,member) suffisent pour 80 % des cas.viewer - ABAC : passer à des policies (Casbin, Cerbos, OPA) quand les règles dépendent d'attributs (région, montant, statut).
- RBAC:全局角色(、
admin、member)可满足80%的场景需求。viewer - ABAC:当规则依赖属性(地区、金额、状态)时,使用策略引擎(Casbin、Cerbos、OPA)。
Pour aller plus loin
拓展阅读
Patterns détaillés par tier, migration tier 1 → tier 2 sans downtime, tests d'isolation (Pest + tenant fixtures), RBAC/ABAC, exemples Laravel + Symfony, checklists par phase : voir.@.claude/skills/multitenant/REFERENCE.md
各层级的详细实现模式、Tier 1到Tier 2的无停机迁移方案、隔离测试(Pest + 租户测试数据)、RBAC/ABAC示例、Laravel + Symfony代码示例、各阶段检查清单:详见。@.claude/skills/multitenant/REFERENCE.md