本文比较了记录最多的 Postgres 租赁模型(共享模式、每个租户基础、专用集群,也称为每个租户数据库与共享数据库),解释了 noisy neighbor机制,然后详细介绍了 Aurabase 如何实现自己的两层模型,并在配置程序代码中进行验证。有关我们在发布性能数据之前应用的方法,请参阅我们的 后端基准测试方法。
如果您正在寻找两个客户端(RLS、策略、service_role)之间的数据泄漏问题,这不是本文的角度:我们的 RLS 比较和项目 的专用数据库详细介绍了这种逻辑隔离。这里,我们讨论的是物理资源:CPU、IO、连接、缓存。
要点
- 共享数据库不一定是共享方案:Aurabase 通过仅共享集群为每个项目提供自己的 Postgres 数据库,即使在其标准级别也是如此。
noisy neighbor会降低物理资源(CPU、IOPS、连接、autovacuum),而不是数据机密性:RLS 无法解决它,这不是它的作用。- Aurabase 配置程序恰好路由到两种架构,并在代码中进行了验证:
FullyDedicated(为项目保留的整个 CNPG 集群,企业级)或SharedClusterDedicated(基于组织的 CNPG 集群的专用基础,从不与其他组织共享)。 - Aurabase 队列集群的默认观察容量阈值为每个集群 1000 个基地,可配置,超过该阈值建议迁移到专用集群。
- 正确的选择取决于您的实际限制(合规性、流量可预测性、预算),而不是“专用总是更好”的条件反射。
三种 Postgres 管理模型,从最共享到最隔离
微软关于多租户SaaS应用程序架构的官方文档区分了三种租户模型,通常称为Silo(每个租户专用资源)、Pool(完全共享资源)和Bridge(两者的混合体,一些是独立的租户,另一些是共享的)。这三个模型在模式、数据库或整个集群级别直接应用于 Postgres。
具体来说,对于 Postgres 后端,这提供了三种不同的架构。共享模式(单个基础、tenant_id列、过滤行的 RLS 策略)是多租户指南中最常见的池模型:经济,但两个客户端之间的边界变成了逐表计算的 SQL 表达式。共享集群上每个租户的基础是中间 Bridge 模型:每个租户都有自己的 Postgres 基础(真正的 CREATE DATABASE命令),但多个基础共存于同一个物理集群上,因此共享 CPU、IO 和连接。每个租户完全专用的集群是完整的筒仓模型:完全隔离的 CPU、RAM 和 IO 资源,通常为具有高合规性或负载问题的租户保留。
| 型号 | 资源隔离 | 储存保温 | 运营努力 |
|---|---|---|---|
| 共享架构(tenant_id + RLS) | 无 | 无(公共表) | 最小(1 个运行基地) |
| 每个租户基础上,共享集群 | 部分(集群CPU/IO) | 总计(自己的基础) | 中等(N 个碱基,1 个簇) |
| 每个租户完全专用的集群 | 总计 | 总计 | 高(每个租户 1 个集群) |
Silo / Pool / Bridge 术语:微软官方文档,多租户 SaaS 架构模式(Azure 架构中心)。
指南中通常缺少基于共享集群上的每个租户的中间模型,这些指南将“每个人的单一基础”和“每个客户端一个服务器”之间的选择呈现为二元。然而,这是 Aurabase 默认使用的,详细信息如下。
这三种模型之间的选择取决于每个多租户架构决策,而不仅仅是 BaaS 提供商:在托管 Postgres(RDS、Cloud SQL 或自托管实例)上构建自己的 SaaS 后端的团队会进行完全相同的仲裁,一旦多个客户端被放置在同一个物理实例上,就会使用相同的争用机制。
吵闹的邻居:资源共享时情况会恶化
noisy neighbor(吵闹的邻居)是消耗不成比例的基础设施共享资源的租户,从而损害同一服务器上的其他租户。该术语来自公共云,但直接适用于共享 Postgres 集群:一个数据库可以降低其他数据库的性能,而无需触及其他数据库的数据。
生产中最常出现的六种机制:
- CPU 争用:昂贵的查询(无索引联接、大规模排序)会消耗内核在集群中所有活动数据库之间共享的 CPU 周期。
- IOPS 争用:备份、
VACUUM FULL或大量导入会使集群的磁盘吞吐量饱和,从而减慢对其他数据库的读取和写入速度。 - 连接耗尽:
max_connections限制整个集群级别的活动连接数量,而不是每个基地。开放过多的基地会降低其他基地的利润。 - Autovacuum 争用:autovacuum 运行时每个集群的工作线程数量有限;具有高写入率的数据库可能会延迟清理另一个数据库的表。
- 缓存驱逐:
shared_buffers是整个集群的单个内存;具有较大工作集的数据库可以逐出较小相邻数据库的缓存页面。 - 共享维护时段:备份、副本故障转移或主要升级适用于整个集群,而不是基于每个基地。
结构图,而不是测量结果:迄今为止,还没有发布这两种拓扑的比较性能数据。
连接预算通常是生产中最明显的症状,甚至在延迟之前也是如此。这是我们的 max_connections 调整指南 和我们的 比较 PgBouncer、Supavisor 和 PgCat的详细主题。
A transaction mode pooler (PgBouncer, Supavisor, PgCat) mitigates connection exhaustion, but does not remove CPU or IOPS contention: it reuses existing server connections, it does not add additional CPU cores or disk throughput to the cluster. Our article on transaction pooling mode details what this mode actually changes, and what it doesn't change.
RLS 隔离数据,而不是资源
行级安全性解决了另一个问题:它防止查询在逻辑级别读取或修改另一个租户的行。它不为特定租户保留 CPU 周期、连接插槽、磁盘吞吐量。
两个租户可以拥有完全无懈可击的 RLS 策略,但同时会互相贬低:吵闹的邻居是物理资源的问题,而不是访问权限的问题。一旦 EPIRB 到位,混淆两者会导致操作安全的错误感觉。
对于逻辑隔离(RLS 策略、 service_role、安全方面项目之间的边界),请参阅我们的专门文章: RLS 和每个项目的专用基础、Aurabase 的多租户隔离选择。本文仍然停留在物理资源层面。
Aurabase 模型,已在代码中验证
配置程序代码 (aura-provisioner) 准确记录了活动 Postgres 项目的两种可能的架构,来自代码称为任务 12 的配置整合: FullyDedicated 和 SharedClusterDedicated。旧的架构粒度模型已从配置路径中删除。
enterprise 级别触发 FullyDedicated:整个 CNPG 集群,为该单个项目保留。所有其他级别(免费、专业、团队)都会路由到 SharedClusterDedicated:项目组织的 CNPG 集群上的成熟 Postgres project_<uuid> 数据库。因此,它不是一个共享模式:即使在标准计划中,您的数据库也是一个完整的 Postgres 数据库,而不是公共表中的一行。共享的是集群(CPU、RAM、磁盘、连接),而不是基础本身。
组织的 CNPG 集群是在配置其第一个 Postgres 项目时创建的,并且从不托管来自其他组织的项目,这是在代码级别锁定的设计选择(org_cluster.rs,创建时组织的 Postgres 咨询锁定)。因此,共享 Aurabase 平台上唯一可能吵闹的邻居是您自己组织的另一个项目,而不是第三方客户的项目。
Aurabase 管理的 PostgreSQL 集群架构规范。
每个集群 1000 个碱基的阈值并不是硬性限制:它是一个可观察性基准,会触发迁移到 FullyDedicated的建议,而不是自动阻止。它不再驱动任何放置决策,每个组织现在只能使用一个集群。
每个集群,无论是专用集群还是集群集群,都公开一个 CNPG Pooler (PgBouncer),它可以吸收两种拓扑中活动连接的部分压力。下面详细介绍了该池程序针对资源争用实际所做的更改。
共享集群的 CPU/RAM 大小在组织之间也不一致:它通过专用函数(FleetSizing::from_org_plan,在 org_cluster.rs中验证)从组织级别派生,而不是从应用于所有级别的单一大小派生。团队级别的组织不会采用与自由级别的组织相同的方式调整集群大小。
这两种架构在 PostgreSQL 16 上运行,而不是版本 17,在生产中使用的 CNPG 镜像的 Dockerfile 中进行了验证。这种版本选择有其自身的调整含义,详细信息请参见我们的 Postgres 16 vs 17 vs 18 比较。
当池化就足够了,当需要专用时
池化并不是一种廉价的妥协。它对应于绝大多数正在开发、启动或适度增长的项目的流量,在这些项目中,专用集群将是额外的成本,而没有可衡量的效益。
| 信号 | 共享就够了 | 专用推荐 |
|---|---|---|
| 正式遵守物理隔离规定(卫生、人力资源、公共部门) | 否 | 是的 |
| 可预测的交通流量,适度的高峰 | 是的 | |
| 不可预测且持续的峰值负载 | 限制的风险 | 是的 |
| 预算紧张,产品处于验证阶段 | 是的 | |
| 要求记录绝缘的合同条款 (DPA) | 否 | 是的 |
对于受记录物理绝缘合同义务约束的项目,我们的 DPA 和 合规性 页面详细介绍了每个级别涵盖的内容。
Bytebase 等多个模式迁移管理工具强调了逐个租户模型的缺点,该缺点是操作性的而不是技术性的:每个迁移都必须在每个基础上逐一应用和验证,即使它们共存于同一集群上也是如此。完全专用的集群并不能消除这一成本,甚至还增加了这一成本:每个集群进行一次迁移来独立监控,而不仅仅是一次。
专门研究多租户软件架构的资源(例如 CodeOpinion)定期将这种中间方法(基于共享基础设施的每个租户)呈现为共享方案和完全专用集群之间的合理折衷,而不是两个极端之间的二元选择。
从共享到专用不需要重写模式或更改引擎:在这两种情况下,它都是 Postgres,具有与我们的 Supabase 到 Aurabase 迁移指南中描述的相同的 pg_dump / pg_restore 链。更改级别仍然是切换操作,而不是应用程序重写。
常见问题
要记住什么
专用库和共享库在数据安全上并不冲突:两种模型都可以在逻辑层面上正确地将一个租户与另一个租户隔离。它们在物理资源(CPU、IOPS、连接、缓存、维护窗口)上相互对立。正是这个计划定义了一个吵闹的邻居,而不是一个写得不好的 RLS 策略。
在配置程序代码中验证的 Aurabase 模型默认保留中间折衷方案:每个项目一个专用的 Postgres 数据库,位于共享集群上,但严格为单个组织保留,并为业务级别保留完全专用的集群。正确的选择取决于您的实际限制,而不是反射,专用始终是最佳选择。