多租户架构基础:概念、模型、隔离级别

多租户,说白了就是一套系统同时服务多个客户(租户),每个租户的数据互相隔离、互不可见。我刚接触这个概念时觉得挺简单——不就是把用户分组吗?真正做起来才发现,隔离怎么切、粒度怎么选、性能怎么平衡,全是坑。这节课我们先打地基,把概念、模型、隔离级别讲透。

1. 多租户到底是什么?

我可以给你一个很直白的定义:多租户是一种软件架构,让单个应用实例能同时服务于多个组织(租户),同时每个租户感觉自己在独享系统。

举个例子,你用过钉钉或飞书吧?成千上万的公司都在用同一套钉钉服务,但A公司看不到B公司的聊天记录,B公司也改不了A公司的审批流程。这就是典型的多租户。

和传统的单租户(每个客户部署一套独立环境)比,多租户最大的优势是资源利用率高、运维成本低。但我遇到过很多团队一开始图省事,把所有租户的数据放在一个表里,只靠一个 tenant_id 字段区分——后来发现一个租户的慢查询能拖垮所有人。嗯,教训挺深的。

核心要点:多租户 ≠ 简单的权限分组。它是数据安全、性能隔离、可扩展性三者的平衡艺术。

2. 主流的多租户模型

根据数据存储和隔离的实现方式,我习惯把多租户模型分为三种。用一张表格对比更清楚:

模型 数据存储方式 隔离强度 资源成本 适用场景
独立数据库 每个租户一个独立DB 最高 最高 金融、医疗、大客户定制
共享数据库·独立Schema 同一个DB,每个租户一个Schema 中等 中等 SaaS中大型企业版
共享数据库·共享表 所有租户在同一个表,用列区分 低 最低 成本敏感、低合规要求

我在做审计SDK改造时,客户要求支持这三种模型动态切换。当时我们的代码写死了 tenant_id 过滤逻辑,切换到独立数据库模型时,连接池和事务管理全得重写。所以我的建议是:一开始就设计一个抽象层,把“租户路由”和“数据访问”拆开,后面换模型时只需要改路由策略,不用动核心业务逻辑。

3. 隔离级别:从数据到性能

很多人以为多租户隔离只关心数据,其实不止。我把它拆成三个维度:

  1. 数据隔离——一个租户不能看到其他租户的数据。
  2. 性能隔离——一个租户的查询风暴不能拖慢其他租户。
  3. 配置隔离——每个租户可以有自己的个性化设置(比如自定义字段、审计规则)。

数据隔离我们刚讲过,三种模型对应不同强度。但性能隔离呢?共享表模型下,如果有一个租户做了全表扫描,带 WHERE tenant_id = 'xxx' 也救不了你——因为全表扫描会锁住整个表。我踩过这个坑,后来我们给审计日志表加了 tenant_id 作为分区键,加上索引,才算压得住。

配置隔离反倒简单,一般用一个 tenant_config 表,每个租户一行JSON配置就行。但要注意:配置变更最好走版本化,否则回滚时很痛苦。

我的经验:选择隔离级别时,别一刀切。可以按租户等级做动态调整——VIP租户用独立数据库,免费版租户用共享表。但路由逻辑必须统一,否则维护成本爆炸。

4. 一张图看懂多租户架构

下面这张SVG图展示了我常用的多租户分层模型。租户标识(tenant_id)从网关层注入,贯穿服务层、数据访问层,直到最底层的存储。橙色节点表示需要识别租户上下文的关键位置。

多租户架构分层模型 租户A Client App 租户B Client App 租户C Client App 网关层:提取 tenant_id(JWT/Header) 服务层:业务逻辑 + 审计SDK(携带 tenant_id) 数据访问层:租户路由 + 多模型适配器 独立数据库 每个租户一个DB 共享DB·独立Schema Schema隔离 共享DB·共享表 tenant_id行级隔离 注意:审计SDK需要在服务层和数据访问层之间传递 tenant_id,确保日志归属正确

5. 避坑指南与总结

多租户的基础说到底就是两件事:标识和隔离。标识靠 tenant_id 贯穿请求链路,隔离靠模型选择和数据访问层适配。

我曾经踩过的坑:

  • 忘记在异步任务、消息队列里传递 tenant_id——审计日志变成孤儿记录。
  • 共享表模型下,查询没带 tenant_id 条件——DBA直接打电话给我,说CPU爆了。
  • 配置隔离用硬编码 switch-case——后来客户要求动态增加租户配置项,代码改得想吐。

核心教训:多租户不是加个字段就完事了,它是架构层面的设计约束,从API网关到存储全链路都得认这个 tenant_id。