Make the MongoDB docs better! We value your opinion. Share your feedback for a chance to win $100.
MongoDB Branding Shape
Click here >
Docs 菜单

为MongoDB Search 构建多租户架构

您可以使用MongoDB Search实现多租户,以便应用程序的单个实例为多个租户提供服务。本页介绍专门应用于MongoDB Search 的设计建议,用于结构化数据和MongoDB Search 索引,以便在保持租户隔离性的同时安全扩展。

重要

本指导假设您可以在单个VPC中并置租户。如果需要严格的网络隔离性,则必须为每个租户维护单独的项目。

根据您的隔离性需求、扩展目标和写入负载要求选择策略。最常见的策略是:

  • 所有租户使用单一集合— 建议大多数工作负载使用此策略。

  • 每个租户一个数据库 - 此策略提供高度隔离性,但会增加索引计数。

  • 每个租户一个集合 不建议对MongoDB搜索工作负载使用此策略。

我们建议应用“所有租户使用一个集合”策略。在此架构中,您将所有租户数据存储在单个数据库内的单个集合中。您可以通过在每个文档中包含 tenant_id字段来区分租户。此字段可以是租户的任何唯一标识符,例如 UUID 或租户名称。

这种集中式方法具有以下优点:

  • 减少索引数量

    由于所有租户数据都存储在一个集合中,因此您只需要一个为所有租户提供文档的索引。可以避免达到集群资源限制。示例,您可以在单个索引中包含 tenant_id 和其他共享或租户特定的字段。

  • 简化维护操作

    由于只有一个集合存储所有数据,因此您不必维护多个集合或跨多个数据库扩展资源。您还可以简化备份、分片和复制操作,因为您只需针对单个数据库中的单个集合。

  • 高效处理查询

    您可以使用 $search.compound.filter 子句中的 等于 操作符在查询中提供 tenant_id 字段作为过滤器。

    注意

    查询结果将仅包含来自匹配租户的文档,从而防止数据跨租户泄漏。

如果您托管的租户股票相似的访问权限模式和资源大小,则此策略还可以使Lucene索引字段数保持较低水平。

重要

当每个租户都有较高的写入负载时,将所有租户并置到单个集合中可能会导致写入争用。在这种情况下,可以考虑将写入次数最多的租户拆分到单独的集合中,以便将写入负载分布到更多的根本的资源。

如果您的大型租户具有独特的资源或索引需求,请使用视图:

  1. 隔离租户。使用带有 操作的 创建视图 $match$expr,以仅使用过滤器过滤租户的文档。

  2. 单独索引。在视图上创建MongoDB Search索引。

    这样,您就可以为大型租户自定义索引定义,而不会影响其他租户使用的共享索引。所有其他租户都可以使用单个索引。

    大量索引会在基础集群上产生大量负载,并可能扰乱您的工作负载。单个集群上的搜索索引硬性限制为 2,500。但是,集群可以支持的索引数量取决于集群层和工作负载。由于索引数量少得多,M10 等较小的集群层可能会出现性能下降或内存不足错误的情况。从少量索引开始,并在扩展时监控集群的资源使用情况。

如果您当前应用每个租户一个集合策略,我们建议扩展基础层级,以防止由于大量索引的资源争用而导致复制延迟 OOM 错误增加。我们还建议迁移到所有租户的同一个集合策略。

如果特定租户有大量写入负载,则可能会导致共享集合发生争用。我们建议您仅将流量最大的租户移动到单独的集合中,以分配 I/O 负载。

如果某个租户需要唯一索引或明显大于其他租户,请使用MongoDB视图:

  • 创建视图,使用

  • $match 仅过滤该租户的文档。

  • 在该视图上创建MongoDB 搜索索引。这样就可以对异常租户进行定制,而不会中断大多数租户使用的共享索引。

在每租户数据库设置中,每个租户都包含自己的数据库。此设置会隔离租户数据,但也会成倍增加集群中的索引数量。如果支持所有租户的数据库超过 2,500,则集群可能会达到 2,500-索引上限。

警告

大量索引会在基础集群上产生大量负载,并可能扰乱您的工作负载。单个集群上的搜索索引硬性限制为 2,500。但是,集群可以支持的索引数量取决于集群层和工作负载。由于索引数量少得多,M10 等较小的集群层可能会出现性能下降或内存不足错误的情况。从少量索引开始,并在扩展时监控集群的资源使用情况。

每个租户的集合策略将每个租户映射到共享数据库中自己的集合。维护每个集合的索引的开销可能会导致:

  • 复制延迟增加

  • 由于资源争用而导致的 OOM 错误

  • 基础层级不稳定

我们不建议对MongoDB Search 使用这种策略,除非租户具有非常可预测的轻量级工作负载。如果您当前使用此策略,迁移到所有租户的一个集合策略。

请考虑以下建议来管理索引字段:

使用动态映射对未知字段进行索引。

多租户应用程序通常需要可搜索、排序、分面和筛选的自定义每租户字段。使用 typeSet 动态索引路径的子字段,以便MongoDB Search 自动选取新字段,而无需重建完整索引。避免对这些字段进行静态索引,因为这样做会触发重建,从而增加 CPU 压力并消耗高达 2 倍索引的磁盘空间。

注意

字段过多的风险

如果不同字段超过 1000 个, Lucene索引性能可能会下降。您可以在MongoDB Search 指标中监控MongoDB Search索引中唯一字段的数量。如果您需要支持一千个或更多字段,我们建议您执行以下操作:

  • 使用视图将租户子集分离到单独的索引中,以便单个Lucene索引中存在的不同字段数保持在 1,000 以下。

  • 限制单个租户可以添加的字段数量。

避免使用嵌入式文档来存储不同的值。

如果使用属性模式为每个租户存储不同的密钥,我们建议您执行以下操作:

  1. 使用视图展平嵌入式文档数组。

  2. 在视图上创建MongoDB搜索索引,并将其存储在磁盘上。

如果不展平文档数组,则需要使用 embeddedDocuments操作符查询(类似于$elemMatch)来查询属性,这比查询展平结构的性能要低。