第 2 章:常见多租户架构模式及隔离策略对比

好,咱们接着聊。上一章我们把多租户的概念和 SDK 权限隔离的痛点理清了。这一章,我打算把常见的几种架构模式搬出来,做个彻底的对比。为什么先讲这个?因为你在设计 SDK 的权限隔离方案前,得先知道你的服务本身是个什么“骨架”。骨架不同,隔离策略的发力点完全不一样。

我个人习惯把多租户的架构模式分成三大类,外加一种常用的变体。这几种模式,我在不同的项目里都亲手踩过坑,今天把心得全倒出来。

三种核心架构模式:隔离的“光谱”

隔离程度不是非黑即白的,它更像一条光谱。从物理隔离到逻辑隔离,性能、成本、复杂度在两端之间摇摆。说白了,你得根据业务场景找个平衡点。

架构模式 隔离粒度 资源占用 数据安全等级 运维复杂度
独立数据库 (Database per Tenant) 最高 最大 最高 中
共享数据库,独立 Schema (Shared DB, Separate Schemas) 中 中 高 高
共享数据库,共享 Schema (Shared DB, Shared Schema) 最低 最小 低 低
混合模式 (Hybrid) 按策略可变 按策略可变 按策略可变 最高

1. 独立数据库模式

这是最“傻”但最安全的做法。每个租户独享一个数据库实例或数据库。想象一下,每个客户都住在自己的独栋别墅里,采光、隐私、隔音都没得说。

我的经验: 有一次接手一个金融 SaaS 项目,合规要求特别严。我们直接选了独立数据库模式。虽然备份脚本写了厚厚一叠,但每次审计都轻松过关。数据完全物理隔离,SDK 的权限根本不需要考虑“串号”的问题,因为上下文是天然割裂的。

优点: 隔离性最强,数据恢复简单(直接恢复某个租户的库就行)。

劣势: 成本高,维护难。你想想看,1000 个租户就得有 1000 个连接池,光是监控这些数据库的健康状态就够你喝一壶的。

2. 共享数据库,独立 Schema 模式

这个模式挺有意思。物理上大家住在同一个数据库里,但逻辑上每个租户有自己的“小院子”(Schema)。表结构可以一样,但数据隔开。

我曾经踩过坑: 为了省事儿,没给 Schema 级权限做固化。结果 DBA 手滑,给 A 租户的 Schema 赋予了跨 Schema 查询权限。虽然业务代码没用到,但这个口子一开,后患无穷。后来我学乖了,直接在 DDL 层面就把 Schema 间的引用权限掐死。

适用场景: 中大型企业客户,对数据隔离有一定要求,但预算没高到能上独立数据库。

3. 共享数据库,共享 Schema 模式

这是最常见的模式,也是最具挑战的模式。所有租户的数据混在一个表里,通过 tenant_id 字段来区分。就像所有人都住在同一个大通铺里,只能靠身上的“身份证”来区分彼此。

-- 典型的共享 Schema 表结构
CREATE TABLE orders (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id VARCHAR(64) NOT NULL,   -- 核心隔离字段
    order_number VARCHAR(128),
    amount DECIMAL(10, 2),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_tenant_order (tenant_id, created_at)
);

这种模式对 SDK 权限隔离的考验最大。你必须在代码层、中间件层、甚至 SQL 层强制注入 tenant_id 过滤条件。少一条,数据就串了。

关键思路: 共享 Schema 模式下,SDK 的职责从“访问控制”变成“强制过滤”。它不是挡门的保安,而是给每一行数据都装上 GPS 定位器,确保你只能看到自己租户的数据。

4. 混合模式

现实往往不按常理出牌。你可能会遇到“大房小户”的情况:几个大客户要用独立库,海量小客户用共享表。这就是混合模式,也叫“分级隔离”。

我建议你把租户分级:VIP 级(独立库)、标准级(共享 Schema)、入门级(单表)。SDK 在做权限判断时,首先检查租户等级,然后路由到对应的隔离策略。

隔离策略的核心对比:从 SDK 视角出发

上面讲的是“房子”怎么建。下面我们关心的是“门禁卡”(SDK)该怎么设计。不同的架构模式,要求 SDK 做不同的事情。

多租户隔离策略选择流程图 SDK 权限隔离策略选择与映射关系 识别租户等级与模式 评估数据隔离级别要求 高安全要求 独立数据库模式 SDK: 连接池路由 中等安全 独立 Schema 模式 SDK: Schema 切换 低安全(通用) 共享 Schema 模式 SDK: 租户ID过滤 执行 SDK 权限隔离策略

你看上面这张图,从左到右,隔离要求在降低,但 SDK 的侵入性却在增加。我个人经验是,选择策略时,别只看安全,还要考虑 SDK 对业务代码的“污染”程度。

策略对比:隔离、性能与研发成本的三角博弈

很多团队一上来就想用“银弹”,比如无状态 JWT 隔离或者纯网关层过滤。实际上,SDK 层的隔离策略通常离不开下面这三种核心手段。我按实际落地效果排了个序。

  1. 数据源路由策略:SDK 在初始化时,根据租户上下文,动态选择对应的数据源。适用于独立数据库模式。
  2. Schema 切换策略:在同一个数据库连接上,执行 USE SchemaName 或修改连接字符串中的 Schema 参数。适用于独立 Schema 模式。
  3. SQL 拦截重写策略:通过 AOP 或 MyBatis 拦截器,在所有 SQL 后强制追加 AND tenant_id = :currentTenantId。适用于共享 Schema 模式。
避坑指南: 我曾经在某个共享 Schema 项目里,用了“应用层过滤”而不是“SQL 层拦截”。也就是查出来一堆数据后,在 Java Stream 里用 filter 方法根据 tenant_id 过滤。你想想看,如果 A 租户有 100 万行数据,B 租户只有 10 行,B 租户的查询却要扫描 A 租户的 100 万行。性能瞬间雪崩。所以,一定要在 SQL 层面完成过滤,不要依赖应用层。

如何根据业务场景做匹配?

没有最好的模式,只有最合适的。我给大家整理了一个简单的决策清单,你可以拿来做参考。

你的业务场景 推荐的隔离模式 SDK 权限侧重点
金融、医疗、政府项目 独立数据库 连接池隔离、密钥独立
企业级 SaaS,客户数量 < 500 独立 Schema Schema 切换、DDL 权限固化
通用型 SaaS,客户数量 > 1000 共享 Schema SQL 拦截、租户 ID 强校验、二级缓存隔离
大客户 + 中小客户并存 混合模式 SDK 策略配置中心、动态路由

嗯,这里要注意一点:混合模式虽然最灵活,但对 SDK 的架构设计挑战最大。你的 SDK 核心需要解析一个“策略配置文件”,里面写着某个租户走哪种隔离方案。这相当于在 SDK 内部做了一个微型的规则引擎,代码复杂度会直线上升。