

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 适用于 PostgreSQL 的多租户 SaaS 分区模型


实现多租户的最佳方法取决于您的 SaaS 应用程序的要求。以下各节演示了在 PostgreSQL 中成功实现多租户的分区模型。

**注意**  
本节中讨论的模型既适用于亚马逊 RDS for PostgreSQL，也适用于兼容 Aurora PostgreSQL 的模型。本节中对 Postgre *S* QL 的引用适用于这两个服务。

您可以在 PostgreSQL 中使用三种高级模型进行 SaaS 分区：思洛分区、桥接模型和池模型。下图总结了思洛存储器和池模型之间的权衡取舍。桥梁模型是筒仓模型和池模型的混合体。


****  

| **分区模型** | **优点** | **劣势** | 
| --- | --- | --- | 
| 筒仓 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/saas-multitenant-managed-postgresql/partitioning-models.html) | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/saas-multitenant-managed-postgresql/partitioning-models.html) | 
| 池 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/saas-multitenant-managed-postgresql/partitioning-models.html) | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/saas-multitenant-managed-postgresql/partitioning-models.html) | 
| 桥梁 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/saas-multitenant-managed-postgresql/partitioning-models.html) | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/saas-multitenant-managed-postgresql/partitioning-models.html) | 

以下各节将更详细地讨论每种模型。

**Topics**
+ [

# PostgreSQL 筒仓模型
](silo.md)
+ [

# PostgreSQL 池模型
](pool.md)
+ [

# PostgreSQL 桥接模型
](bridge.md)
+ [

# 决策矩阵
](matrix.md)

# PostgreSQL 筒仓模型


思洛模型是通过为应用程序中的每个租户预置一个 PostgreSQL 实例来实现的。筒仓模型在租户性能和安全隔离方面表现出色，并且完全消除了邻居*噪音*现象。当一个租户对系统的使用影响另一个租户的性能时，就会出现邻居噪音现象。思洛模型允许您专门为每个租户量身定制性能，并有可能将停机限制在特定租户的孤岛上。但是，通常推动采用孤岛模式的是严格的安全和监管限制。这些限制可能是由SaaS客户激发的。例如，由于内部限制，SaaS客户可能会要求隔离其数据，而SaaS提供商可能会额外付费提供此类服务。

 ![\[SaaS PostgreSQL silo model\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/saas-multitenant-managed-postgresql/images/saas-postgresql-silo.png) 

尽管在某些情况下可能需要使用筒仓模型，但它有许多缺点。通常很难以经济实惠的方式使用孤岛模型，因为管理多个 PostgreSQL 实例的资源消耗可能很复杂。此外，此模型中数据库工作负载的分布式特性使得维护租户活动的集中视图变得更加困难。管理如此多的独立操作的工作负载会增加运营和管理开销。孤岛模式还使租户入职变得更加复杂和耗时，因为您必须配置租户特定的资源。此外，整个 SaaS 系统可能更难扩展，因为租户专用的 PostgreSQL 实例数量不断增加，需要更多的操作时间来管理。最后一个考虑因素是，应用程序或数据访问层必须维护租户与其关联的 PostgreSQL 实例的映射，这增加了实现此模型的复杂性。

# PostgreSQL 池模型


池模型是通过配置单个 PostgreSQL 实例（Amazon RDS 或 Aurora）并[使用行级安全 (RL](https://aws.amazon.com/blogs/database/multi-tenant-data-isolation-with-postgresql-row-level-security/) S) 来维护租户数据隔离来实现的。RLS 策略限制`SELECT`查询返回表中的哪些行，或者哪些行受到`INSERT``UPDATE`、和`DELETE`命令的影响。池模型将所有租户数据集中在单个 PostgreSQL 架构中，因此成本效益要高得多，维护所需的运营开销也更少。由于该解决方案是集中化的，因此监控该解决方案也要简单得多。但是，在池模型中监控租户特定的影响通常需要在应用程序中使用一些额外的工具。这是因为 PostgreSQL 默认不知道哪个租户在消耗资源。由于不需要新的基础架构，因此可以简化租户入门。这种敏捷性可以更轻松地完成快速、自动化的租户入职工作流程。

 ![\[SaaS PostgreSQL pool model\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/saas-multitenant-managed-postgresql/images/saas-postgresql-pool.png) 

尽管池模式通常更具成本效益且更易于管理，但它确实有一些缺点。在泳池模型中无法完全消除邻居噪音现象。但是，可以通过确保 PostgreSQL 实例上有适当的资源可用以及使用策略减少 PostgreSQL 中的负载（例如将查询卸载到只读副本或 Amazon）来缓解这种情况。 ElastiCache有效的监控在应对租户性能隔离问题方面也起着作用，因为应用程序工具可以记录和监控租户特定的活动。最后，一些SaaS客户可能认为RLS提供的逻辑分离是不够的，他们可能会要求采取额外的隔离措施。

# PostgreSQL 桥接模型


PostgreSQL 桥接模型是池化方法和孤立方法的组合。与池化模型一样，您可以为每个租户配置一个 PostgreSQL 实例。为了保持租户数据隔离，您可以使用 PostgreSQL 逻辑结构。在下图中，PostgreSQL 数据库用于在逻辑上分离数据。

**注意**  
PostgreSQL 数据库不引用单独的 Amazon RDS for PostgreSQL 或兼容 Aurora PostgreSQL 的数据库实例。相反，它指的是用于分离数据的PostgreSQL数据库管理系统的逻辑结构。

 ![\[SaaS PostgreSQL bridge model with separate databases\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/saas-multitenant-managed-postgresql/images/saas-postgresql-bridge-dbs.png) 

您还可以使用单个 PostgreSQL 数据库来实现桥接模型，每个数据库中都有租户特定的架构，如下图所示。

 ![\[SaaS PostgreSQL bridge model with separate schemas\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/saas-multitenant-managed-postgresql/images/saas-postgresql-bridge-schemas.png) 

与池模型一样，桥梁模型存在同样的噪音邻居和租户性能隔离问题。它还要求为每个租户配置单独的数据库或架构，从而产生一些额外的操作和配置开销。它需要有效的监控，以便对租户的绩效问题做出快速反应。它还需要应用程序工具来监控租户特定的使用情况。总体而言，桥接模型可以看作是 RLS 的替代方案，它需要新的 PostgreSQL 数据库或架构，从而稍微增加租户的入职工作量。与孤岛模型一样，应用程序或数据访问层必须维护租户与其关联的 PostgreSQL 数据库或架构的映射。

# 决策矩阵


要决定应在 PostgreSQL 中使用哪种多租户 SaaS 分区模型，请查阅以下决策矩阵。矩阵分析了以下四个分区选项：
+ 思洛存储器 — 每个租户都有单独的 PostgreSQL 实例或集群。
+ 使用单独的数据库进行桥接 — 在单个 PostgreSQL 实例或集群中，每个租户都有一个单独的数据库。
+ 使用单独的架构进行桥接 — 在单个 PostgreSQL 数据库、单个 PostgreSQL 实例或集群中，每个租户都有单独的架构。
+ 池 — 单个实例和架构中租户的共享表。


****  

| **** | **筒仓** | **使用单独的数据库进行桥接** | **使用单独架构进行桥接** | **池** | 
| --- | --- | --- | --- | --- | 
| 使用案例 | 通过完全控制资源使用情况来隔离数据是一项关键要求，或者您的租户规模非常大，并且对性能非常敏感。 | 数据隔离是一项关键要求，并且需要对租户的数据进行有限的交叉引用，或者不需要交叉引用。 | 租户数量适中，数据量适中。如果您必须交叉引用租户的数据，则这是首选模型。 | 大量租户，每个租户的数据量较少。 | 
| 新租户入职敏捷性 | 很慢。（每个租户都需要一个新的实例或集群。） | 速度适中。（需要为每个租户创建一个新数据库来存储架构对象。） | 速度适中。（需要为每个租户创建一个新的架构来存储对象。） | 最快的选择。（需要最少的设置。） | 
| 数据库连接池配置工作量和效率 | 需要付出大量努力。（每个租户一个连接池。） 效率较低。（租户之间不共享数据库连接。）  | 需要付出大量努力。（除非您使用 [Amazon RDS 代理](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/rds-proxy.html)，否则每个租户只能配置一个连接池。）  效率较低。（租户和连接总数之间不共享数据库连接。 根据数据库实例类别，所有租户的使用量受到限制。） | 所需的精力更少。（所有租户都有一个连接池配置。）  效率适中。（仅在会话池模式下通过`SET ROLE`或`SET SCHEMA`命令重用连接。 `SET`使用 Amazon RDS 代理时，命令还会导致会话固定，但是为了提高效率，可以取消客户端连接池，并且可以为每个请求建立直接连接。） | 所需的精力最少。 效率最高。（所有租户都有一个连接池，所有租户都能高效地重复使用连接。 数据库连接限制基于数据库实例类别。） | 
| 数据库维护（[真空管理](https://www.postgresql.org/docs/current/routine-vacuuming.html)）和资源使用 | 更简单的管理。 | 中等复杂性。（可能会导致大量资源消耗，因为之后必须为每个数据库启动真空工作程序vacuum\$1naptime，这会导致自动真空启动器 CPU 使用率过高。 清理每个数据库的 PostgreSQL 系统目录表可能还会带来额外的开销。） | 大型 PostgreSQL 系统目录表。（总pg\$1catalog规模以十为单位 GBs，视租户数量和关系而定。 可能需要修改与吸尘相关的参数以控制表格膨胀。） | 表可能很大，具体取决于租户数量和每个租户的数据。（可能需要修改与吸尘相关的参数以控制表膨胀。） | 
| 扩展管理工作 | 大量工作（针对不同实例中的每个数据库）。 | 大量工作（在每个数据库级别）。 | 最少的工作量（在公共数据库中使用一次）。 | 最少的工作量（在公共数据库中使用一次）。 | 
| 更改部署工作 | 付出了巨大的努力。（连接到每个单独的实例并推出更改。） | 付出了巨大的努力。（Connect 连接到每个数据库和架构，然后推出更改。） | 适度的努力。（Connect 连接到公用数据库并发布每个架构的更改。） | 最少的努力。（Connect 连接到公共数据库并推出更改。） | 
| 变更部署-影响范围 | 最小。（单租户受到影响。） | 最小。（单租户受到影响。） | 最小。（单租户受到影响。） | 非常大。（所有租户都受到影响。） | 
| 查询绩效管理和工作量 | 可管理的查询性能。 | 可管理的查询性能。 | 可管理的查询性能。 | 维护查询性能可能需要花费大量精力。（随着时间的推移，由于表的大小增加，查询的运行速度可能会更慢。 您可以使用表分区和数据库分片来保持性能。） | 
| 跨租户资源影响 | 没有影响。（租户之间不共享资源。） | 影响适中。（租户共享公共资源，例如实例 CPU 和内存。） | 影响适中。（租户共享公共资源，例如实例 CPU 和内存。） | 冲击力很大。（租户在资源、锁冲突等方面相互影响。） | 
| 租户级调整（例如，为每个租户创建其他索引或为特定租户调整数据库参数） | 可能的。 | 有点可能。（可以对每个租户进行架构级别的更改，但数据库参数在所有租户中都是全局的。） | 有点可能。（可以对每个租户进行架构级别的更改，但数据库参数在所有租户中都是全局的。） | 不可能。（表格由所有租户共享。） | 
| 重新平衡对性能敏感的租户的工作量 | 最小。（无需重新平衡。 扩展服务器和 I/O 资源以应对这种情况。） | 中等。（使用逻辑复制或导pg\$1dump出数据库，但停机时间可能会很长，具体取决于数据大小。 您可以使用 Amazon RDS for PostgreSQL 中的可传输数据库功能更快地在实例之间复制数据库。） | 中等，但可能涉及长时间的停机时间。（使用逻辑复制或导pg\$1dump出架构，但停机时间可能会很长，具体取决于数据大小。） | 意义重大，因为所有租户共享相同的表。（对数据库进行分片需要将所有内容复制到另一个实例，并需要执行额外的步骤来清理租户数据。）  很可能需要更改应用程序逻辑。 | 
| 主要版本升级导致数据库停机 | 标准停机时间。（取决于 PostgreSQL 系统目录的大小。） | 停机时间可能会更长。（根据系统目录的大小，时间会有所不同。 PostgreSQL 系统目录表也会在数据库之间复制） | 停机时间可能会更长。（根据 PostgreSQL 系统目录的大小，时间会有所不同。） | 标准停机时间。（取决于 PostgreSQL 系统目录的大小。） | 
| 管理开销（例如，数据库日志分析或备份作业监控） | 重大努力 | 最少的努力。 | 最少的努力。 | 最少的努力。 | 
| 租户级别的可用性 | 最高。（每个租户都出现故障并独立恢复。） | 影响范围更大。（如果出现硬件或资源问题，所有租户都会失败并一起恢复。） | 影响范围更大。（如果出现硬件或资源问题，所有租户都会失败并一起恢复。） | 影响范围更大。（如果出现硬件或资源问题，所有租户都会失败并一起恢复。） | 
| 租户级别的备份和恢复工作 | 最少的努力。（可以独立备份和恢复每个租户。） | 适度的努力。（对每个租户使用逻辑导出和导入。 需要一些编码和自动化。） | 适度的努力。（对每个租户使用逻辑导出和导入。 需要一些编码和自动化。） | 付出了巨大的努力。（所有租户共享相同的桌子。） | 
| 租户级别的恢复工作 point-in-time | 最少的努力。（使用快照使用时间点恢复，或者在 Amazon Aurora 中使用回溯功能。） | 适度的努力。（使用快照恢复，然后使用导出/导入。 但是，这将是一个缓慢的操作。） | 适度的努力。（使用快照恢复，然后使用导出/导入。 但是，这将是一个缓慢的操作。） | 繁重的工作量和复杂性。 | 
| 统一架构名称 | 每个租户的架构名称相同。 | 每个租户的架构名称相同。 | 每个租户的架构不同。 | 通用架构。 | 
| 按租户自定义（例如，特定租户的其他表格列） | 可能的。 | 可能的。 | 可能的。 | 很复杂（因为所有租户共享相同的表）。 | 
| 对象关系映射 (ORM) 层的目录管理效率（例如 Ruby） | 高效（因为客户端连接是特定于租户的）。 | 高效（因为客户端连接是特定于数据库的）。 | 效率适中。（根据所使用的 ORM、 user/role 安全模型和search\$1path配置，客户端有时会缓存所有租户的元数据，从而导致数据库连接内存使用率过高。） | 高效（因为所有租户共享相同的表）。 | 
| 合并租户报告工作 | 付出了巨大的努力。（您必须使用外部数据包装器 [FDWs] 来整合所有租户中的数据，或者提取、转换和加载 [ETL] 到另一个报告数据库。） | 付出了巨大的努力。（您必须使用 FDWs 将所有租户或 ETL 中的数据整合到另一个报告数据库中。） | 适度的努力。（您可以使用并集来聚合所有架构中的数据。） | 最少的努力。（所有租户数据都在同一个表中，因此报告很简单。） | 
| 用于报告的租户专用只读实例（例如，基于订阅） | 最少的努力。（创建只读副本。） | 适度的努力。（您可以使用逻辑复制或 AWS Database Migration Service [AWS DMS] 进行配置。） | 适度的努力。（您可以使用逻辑复制或 AWS DMS 进行配置。） | 很复杂（因为所有租户共享相同的表）。 | 
| 数据隔离 | 最好。 | 更好。（您可以使用 PostgreSQL 角色管理数据库级别的权限。） | 更好。（您可以使用 PostgreSQL 角色管理架构级别的权限。） | 更糟糕的是。（由于所有租户共享相同的表，因此必须实现诸如行级安全 [RLS] 之类的功能以实现租户隔离。） | 
| 租户特定的存储加密密钥 | 可能的。（每个 PostgreSQL 集群都可以有 AWS Key Management Service 自己的AWS KMS[] 密钥用于存储加密。） | 不可能。（所有租户共享相同的 KMS 密钥进行存储加密。） | 不可能。（所有租户共享相同的 KMS 密钥进行存储加密。） | 不可能。（所有租户共享相同的 KMS 密钥进行存储加密。） | 
| 使用 AWS Identity and Access Management (IAM) 对每个租户进行数据库身份验证 | 可能的。 | 可能的。 | 可能（通过为每个架构设置单独的 PostgreSQL 用户）。 | 不可能（因为所有租户共享表）。 | 
| 基础设施成本 | 最高（因为没有共享任何内容）。 | 中等。 | 中等。 | 最低。 | 
| 数据重复和存储使用情况 | 所有租户中总量最高。（PostgreSQL 系统目录表以及应用程序的静态和常用数据在所有租户之间复制。） | 所有租户中总量最高。（PostgreSQL 系统目录表以及应用程序的静态和常用数据在所有租户之间复制。） | 中等。（应用程序的静态和公共数据可以位于通用架构中，并可由其他租户访问。） | 最小。（没有数据重复。 应用程序的静态数据和公共数据可以位于同一个架构中。） | 
| 以租户为中心的监控（快速找出哪个租户导致了问题） | 最少的努力。（由于每个租户都是单独监控的，因此可以很容易地检查特定租户的活动。） | 适度的努力。（由于所有租户共享相同的物理资源，因此您必须应用额外的筛选来检查特定租户的活动。） | 适度的努力。（由于所有租户共享相同的物理资源，因此您必须应用额外的筛选来检查特定租户的活动。） | 付出了巨大的努力。（由于所有租户共享包括表在内的所有资源，因此您必须使用绑定变量捕获来检查特定 SQL 查询属于哪个租户。） | 
| 集中管理和 health/activity 监控 | 重大努力（建立中央监视和中央指挥中心）。 | 工作量适中（因为所有租户共享同一个实例）。 | 工作量适中（因为所有租户共享同一个实例）。 | 工作量最少（因为所有租户共享相同的资源，包括架构）。 | 
| 对象标识符 (OID) 和交易 ID (XID) 环绕的可能性 | 最小。 | 高。（因为 OID，XID 是一个单个 PostgreSQL 集群范围的计数器，因此在物理数据库之间进行有效清理可能会出现问题）。 | 中等。（因为 OID，XID 是一个单个 PostgreSQL 集群范围的计数器）。 | 高。（例如，单个表可以达到 TOAST OID 上限 40 亿，具体取决于 out-of-line列数。） | 