第二节 使用模式隔离或多租户扩展

目标:了解并评估“每个租户一个 Schema”的多租户隔离模型,并介绍像 TenantKit 这样的扩展如何简化这种模式的管理。

除了使用行级安全策略(RLS)在共享表中隔离数据外,另一种常见的多租户架构模式是为每个租户创建一个独立的 Schema


“每个租户一个 Schema”模型

工作原理:

  1. 当一个新租户注册时,系统会自动地在数据库中创建一个以该租户的唯一标识(如 tenant_acme, tenant_globex)命名的 Schema。
  2. 所有属于该租户的表(users, products 等)都会被创建在这个专属的 Schema 内。
  3. 当一个用户登录并发起请求时,应用层会根据其租户身份,将数据库连接的**搜索路径(search_path)**设置为该租户的 Schema。

示例: 当来自 ‘acme’ 公司的 Alice 登录后,应用执行的第一个数据库命令是:

1
SET search_path TO tenant_acme, public;

在此之后,所有不带 Schema 前缀的查询(如 SELECT * FROM products)都会自动地、透明地指向 tenant_acme.products 这张表。由于 search_path 的限制,Alice 根本无法访问到 tenant_globex.products


优点与缺点

优点:

  1. 强隔离性:数据在逻辑上是完全隔离的,每个租户拥有自己的一套表。这提供了比 RLS 更直观、更强的安全感。
  2. 易于备份与恢复:可以非常容易地只备份或恢复单个租户的数据(pg_dump -n tenant_acme)。
  3. 租户级定制:可以为某个特定的大客户(租户)的 Schema 添加自定义的表、列或索引,而不会影响其他租户。
  4. 无查询开销:与 RLS 不同,这种模式在查询时没有额外的策略检查开销。

缺点:

  1. 管理复杂性
    • Schema 迁移:当需要对表结构进行变更(ALTER TABLE)时,你必须编写脚本来遍历所有租户的 Schema,并对其中的每一张表执行变更。这是一个非常复杂且容易出错的过程。
    • 连接数问题:如果租户数量巨大(成千上万),数据库中会存在海量的表对象,这可能会给 PostgreSQL 的系统目录带来压力,并可能增加连接池和查询规划的开销。
  2. 跨租户查询困难:要进行跨所有租户的全局统计分析(例如,计算整个平台的总用户数),需要编写复杂的动态 SQL 来遍历所有 tenant_* Schema 并用 UNION ALL 将结果合并起来。

对比:RLS vs. Schema-per-Tenant

特性RLS (共享表)Schema-per-Tenant
数据隔离级别逻辑行级别逻辑表/Schema级别 (更强)
实现复杂度中等 (需要定义 Policy)高 (需要管理 Schema 迁移)
查询性能有轻微开销无开销
备份/恢复较复杂 (需要过滤 tenant_id)简单
Schema 变更简单 (只需修改一次)非常复杂
跨租户分析简单非常复杂
适用租户规模非常大 (数万甚至更多)中小规模 (几百到几千)

选择建议:

  • 如果你的应用租户数量可能非常巨大,并且跨租户的全局分析是一个核心需求,那么 RLS 方案通常更具扩展性和可维护性。
  • 如果你的应用租户数量有限,每个租户的数据隔离性要求极高,并且需要为不同租户进行 Schema 定制,那么 Schema-per-Tenant 模型可能更合适。

使用扩展简化管理:TenantKit

手动管理成百上千个 Schema 的创建和迁移是一场噩梦。社区也为此提供了一些解决方案,例如 TenantKit

TenantKit 是一个开源的 Ruby on Rails 引擎(或适用于其他框架的库),它旨在自动化 Schema-per-Tenant 模型的管理。

TenantKit 提供的功能:

  • 租户创建/删除:当你在 tenants 表中创建一条新记录时,它会自动地创建对应的 Schema 和内部的表结构。
  • 请求路由:根据请求的域名或子域名,自动地将数据库连接的 search_path 切换到正确的租户 Schema。
  • 数据库迁移辅助:提供工具来帮助你在所有租户的 Schema 上统一执行数据库迁移脚本。

虽然 TenantKit 这样的工具极大地简化了开发工作,但它并不能完全消除 Schema-per-Tenant 模型固有的管理复杂性,特别是在处理失败的迁移和大规模集群时。


📌 小结

Schema-per-Tenant 是一种提供了极强逻辑隔离的多租户模型,它在租户级备份和定制方面具有明显优势。然而,这种优势是以极高的 Schema 管理复杂性为代价的。

在现代 SaaS 应用开发中,随着租户数量的快速增长和对敏捷迭代(快速变更表结构)的需求,RLS 方案因其更简单的维护模型和更好的扩展性,正变得越来越受欢迎

在做架构选型时,你需要仔细评估业务的长期需求:是对物理隔离和租户级定制的需求更迫切,还是对平滑扩展和易于维护的需求更重要。