多租户架构基础:概念、模型、隔离级别
多租户,说白了就是一套系统同时服务多个客户(租户),每个租户的数据互相隔离、互不可见。我刚接触这个概念时觉得挺简单——不就是把用户分组吗?真正做起来才发现,隔离怎么切、粒度怎么选、性能怎么平衡,全是坑。这节课我们先打地基,把概念、模型、隔离级别讲透。
1. 多租户到底是什么?
我可以给你一个很直白的定义:多租户是一种软件架构,让单个应用实例能同时服务于多个组织(租户),同时每个租户感觉自己在独享系统。
举个例子,你用过钉钉或飞书吧?成千上万的公司都在用同一套钉钉服务,但A公司看不到B公司的聊天记录,B公司也改不了A公司的审批流程。这就是典型的多租户。
和传统的单租户(每个客户部署一套独立环境)比,多租户最大的优势是资源利用率高、运维成本低。但我遇到过很多团队一开始图省事,把所有租户的数据放在一个表里,只靠一个 tenant_id 字段区分——后来发现一个租户的慢查询能拖垮所有人。嗯,教训挺深的。
核心要点:多租户 ≠ 简单的权限分组。它是数据安全、性能隔离、可扩展性三者的平衡艺术。
2. 主流的多租户模型
根据数据存储和隔离的实现方式,我习惯把多租户模型分为三种。用一张表格对比更清楚:
| 模型 | 数据存储方式 | 隔离强度 | 资源成本 | 适用场景 |
|---|---|---|---|---|
| 独立数据库 | 每个租户一个独立DB | 最高 | 最高 | 金融、医疗、大客户定制 |
| 共享数据库·独立Schema | 同一个DB,每个租户一个Schema | 中等 | 中等 | SaaS中大型企业版 |
| 共享数据库·共享表 | 所有租户在同一个表,用列区分 | 低 | 最低 | 成本敏感、低合规要求 |
我在做审计SDK改造时,客户要求支持这三种模型动态切换。当时我们的代码写死了 tenant_id 过滤逻辑,切换到独立数据库模型时,连接池和事务管理全得重写。所以我的建议是:一开始就设计一个抽象层,把“租户路由”和“数据访问”拆开,后面换模型时只需要改路由策略,不用动核心业务逻辑。
3. 隔离级别:从数据到性能
很多人以为多租户隔离只关心数据,其实不止。我把它拆成三个维度:
- 数据隔离——一个租户不能看到其他租户的数据。
- 性能隔离——一个租户的查询风暴不能拖慢其他租户。
- 配置隔离——每个租户可以有自己的个性化设置(比如自定义字段、审计规则)。
数据隔离我们刚讲过,三种模型对应不同强度。但性能隔离呢?共享表模型下,如果有一个租户做了全表扫描,带 WHERE tenant_id = 'xxx' 也救不了你——因为全表扫描会锁住整个表。我踩过这个坑,后来我们给审计日志表加了 tenant_id 作为分区键,加上索引,才算压得住。
配置隔离反倒简单,一般用一个 tenant_config 表,每个租户一行JSON配置就行。但要注意:配置变更最好走版本化,否则回滚时很痛苦。
我的经验:选择隔离级别时,别一刀切。可以按租户等级做动态调整——VIP租户用独立数据库,免费版租户用共享表。但路由逻辑必须统一,否则维护成本爆炸。
4. 一张图看懂多租户架构
下面这张SVG图展示了我常用的多租户分层模型。租户标识(tenant_id)从网关层注入,贯穿服务层、数据访问层,直到最底层的存储。橙色节点表示需要识别租户上下文的关键位置。
5. 避坑指南与总结
多租户的基础说到底就是两件事:标识和隔离。标识靠 tenant_id 贯穿请求链路,隔离靠模型选择和数据访问层适配。
我曾经踩过的坑:
- 忘记在异步任务、消息队列里传递 tenant_id——审计日志变成孤儿记录。
- 共享表模型下,查询没带 tenant_id 条件——DBA直接打电话给我,说CPU爆了。
- 配置隔离用硬编码 switch-case——后来客户要求动态增加租户配置项,代码改得想吐。
核心教训:多租户不是加个字段就完事了,它是架构层面的设计约束,从API网关到存储全链路都得认这个 tenant_id。