1. 多租户架构基础:理解概念、隔离与模型选择
说到多租户,很多人第一反应就是“一个软件给好多家客户用”。嗯,对,也没错。但如果你只想到这层,那离真正设计好一个多租户系统还差得远。
我先讲个我自己的经历。几年前我带团队做一个SaaS平台,一开始图省事,所有客户的数据全塞在同一张表里,靠一个tenant_id字段区分。结果呢?上线不到三个月,某家客户的运营人员不小心写了个全表扫描的SQL,直接把其他客户的页面都拖垮了。那一周,我基本没睡过整觉。
所以说,多租户不光是“能共用”,更重要的是“能隔离”。而隔离到什么程度,怎么个隔法,就是本章要聊透的东西。
1.1 多租户核心概念
多租户(Multi-Tenancy)本质上是一种软件架构模式。一个运行实例,服务N个租户。每个租户感觉自己是独占的,但实际上底层资源是共享的。租户可以是企业、部门、项目组,甚至一个独立用户。
- 租户(Tenant):逻辑上的客户单位。每个租户有自己的用户、配置、数据。
- 租户标识:通常用
tenant_id在请求链路中传递,贯穿所有服务。 - 隔离边界:决定了租户之间能共享什么、不能共享什么。边界越强,安全性越高,成本也越高。
你想想看,租户之间最怕什么?怕数据串,怕性能抢,怕配置污染。所以设计的第一步,就是明确你要隔离到什么级别。
1.2 隔离级别:从“软”到“硬”
隔离级别我习惯分为三个层次。每个层次解决不同的问题,也带来不同的代价。
| 隔离级别 | 代表方案 | 数据安全 | 资源隔离 | 运维复杂度 | 成本 |
|---|---|---|---|---|---|
| 软隔离 | 共享数据库 + 行级标识 | ★★ | ★ | ★ | 最低 |
| 中隔离 | 共享数据库 + 独立Schema | ★★★ | ★★ | ★★ | 中等 |
| 硬隔离 | 独立数据库/独立实例 | ★★★★★ | ★★★★★ | ★★★★ | 最高 |
软隔离:所有租户的数据混在同一张表,靠 tenant_id 字段来区分。查询时WHERE tenant_id = ?。优点是省资源,一台数据库能塞几百个租户。缺点也明显——一个写错where条件,或者索引没命中,整个表被锁,所有租户一起遭殃。我前面提到的那个事故,就是软隔离的典型翻车案例。
中隔离:每个租户一个独立的Schema(可以说是数据库里的“文件夹”)。表结构相同,但数据物理上分开。查询时不需要 tenant_id 过滤,因为默认就在对应Schema里。隔离性比软隔离好不少,但数据库实例还是共享的,CPU和内存还是会互相影响。
硬隔离:每个租户一个独立的数据库实例,甚至是独立的虚拟机或容器。这是最彻底的方式,但也最烧钱。一般只有对安全合规要求极高的金融、医疗客户才会用。我记得有个做支付合规的项目,客户明确要求“你们的数据库不能跟我竞争对手的放在同一台服务器上”,那只能走硬隔离。
核心公式:
隔离强度 ∝ 安全性 × 成本 × 运维难度
没有最好的方案,只有最适合你业务场景的方案。
1.3 模型选择:三种主流方案
隔离级别决定了“隔到什么程度”,而模型选择决定了“用什么方式隔”。业界主流有三种模型,我把它们的差异整理成一张表。
| 模型 | 实现方式 | 典型场景 | 审计日志隔离难度 | 扩展性 |
|---|---|---|---|---|
| 共享表(Share Table) | 所有租户共用一张表,tenant_id 行级隔离 |
中小型SaaS,租户数多且单个数据量小 | 高(依赖应用层过滤) | 差(表太大后索引维护困难) |
| 共享数据库·独立Schema(Share Database, Separate Schema) | 每个租户一个Schema,表结构相同 | 中型SaaS,租户数几百到几千 | 中等(按库或Schema自然分隔) | 中等(Schema数量有限制) |
| 独立数据库(Separate Database) | 每个租户一个独立数据库 | 大型客户/合规要求高的场景 | 低(天然物理隔离) | 好(可水平拆分) |
我的建议:对于《租户间审计日志隔离》这个课题,审计日志的隔离要求通常比业务数据更严格。如果一个租户能看到另一个租户的日志,那就是合规事故。所以哪怕业务数据用共享表,审计日志也建议至少做到中隔离(独立Schema)或硬隔离(独立数据库)。我见过太多客户因为审计日志漏隔离被罚钱的案例。
1.4 审计日志的特别之处
为什么审计日志对隔离的要求更高?因为审计日志本身就是为了“追溯”和“取证”而存在的。如果隔离没做好,日志就失去了可信度。想象一下,A租户的审计日志里混进了B租户的运维操作记录——到时候做内审,法律上完全站不住脚。
在实际项目中,我一般按以下原则决策:
- 可追溯:每一条日志必须能明确追溯到租户和操作人。
- 不可篡改:日志写入后,应用层不应该提供修改或删除接口。即使为了归档,也只能追加。
- 隔离原子性:如果选择软隔离,
tenant_id字段必须有强制索引,且所有查询都走索引。我曾经因为漏加一个索引,导致日志查询跨了租户,还好是内网测试阶段发现的……
避坑提醒:我曾经在项目中用了共享表做审计日志,然后做了个“超级管理员”角色可以查看所有租户的日志。结果产品手册没写清楚,普通租户的管理员以为也能看所有日志,投诉到法务。后来我们老老实实改成了独立Schema,才消停。所以,不要相信“代码里写好了过滤条件”这种话,物理隔离才是真的隔离。
1.5 用SVG清晰理清选择逻辑
下面这张图梳理了从“租户需求”到“模型选择”再到“审计日志隔离实现”的决策流程,帮你一次性看透全貌。
1.6 选型实战:一个简单公式
在实际决策时,我通常用下面这个简单的维度打分:
总得分 = 租户数权重 × 安全合规权重 × 运维能力权重
租户数权重:
租户 < 50 → 3(倾向硬隔离)
50-500 → 2(可中隔离)
>500 → 1(倾向于软隔离或中隔离)
安全合规权重:
金融/医疗/政府 → 3(必须硬隔离或中隔离+审计专用)
一般企业 → 2
内部工具 → 1
运维能力权重:
专职DBA团队 → 3(可扛硬隔离运维)
研发兼职运维 → 2
无运维 → 1(尽量软隔离或SaaS托管)
把三个分数相乘,如果总分 ≥ 12,我建议至少上中隔离;如果 ≥ 18,那大概率得用硬隔离。当然,这只是启动时的判断,随着业务发展,你要做迁移预案。
记住:审计日志的隔离方案,最好在系统设计第一天就定下来。后面改隔离模型,比改业务数据隔离要痛苦得多——因为日志量巨大,而且可能有法律效力要求不能回退。你自己体会一下这个坑。