您可以使用MongoDB Search实现多租户,以便应用程序的单个实例为多个租户提供服务。本页介绍专门应用于MongoDB Search 的设计建议,用于结构化数据和MongoDB Search 索引,以便在保持租户隔离性的同时安全扩展。
重要
本指导假设您可以在单个 VPC中共置租户。如果需要严格的网络隔离性,则必须为每个租户维护单独的项目。
选择策略
根据您的隔离性需求、扩展目标和写入负载要求选择策略。最常见的策略是:
适用于所有租户的单一集合(推荐)
我们建议应用“所有租户使用一个集合”策略。在此架构中,您将所有租户数据存储在单个数据库内的单个集合中。您可以通过在每个文档中包含tenant_id 字段来区分租户。此字段可以是租户的任何唯一标识符,例如 UUID 或租户名称。
这种集中式方法具有以下优点:
减少索引数量
由于所有租户数据都存储在一个集合中,因此您只需要一个为所有租户提供文档的索引。可以避免达到集群资源限制。示例,您可以在单个索引中包含
tenant_id和其他共享或租户特定的字段。简化维护操作
由于只有一个集合存储所有数据,因此您不必维护多个集合或跨多个数据库扩展资源。您还可以简化备份、分片和复制操作,因为您只需针对单个数据库中的单个集合。
高效处理查询
您可以使用
tenant_id$search.compound.filter子句中的 等于运算操作符在查询中提供 字段作为过滤。注意
查询结果将仅包含来自匹配租户的文档,从而防止数据跨租户泄漏。
如果您托管的租户股票相似的访问权限模式和资源大小,则此策略还可以使Lucene索引字段数保持较低水平。
重要
当每个租户都有较高的写入负载时,将所有租户并置到单个集合中可能会导致写入争用。在这种情况下,可以考虑将写入次数最多的租户拆分到单独的集合中,以便将写入负载分布到更多的根本的资源。
单独索引。在视图上创建MongoDB Search索引。
这样,您就可以为大型租户自定义索引定义,而不会影响其他租户使用的共享索引。所有其他租户都可以使用单个索引。
我们建议每个集群不要超过 2500 个索引,因为这会在基础集群上产生高负载,并有扰乱数据库工作负载的风险。
如果您当前应用每个租户一个集合策略,我们建议扩展基础层级,以防止由于大量索引的资源争用而导致复制延迟 OOM 错误增加。我们还建议迁移到所有租户的同一个集合策略。
处理高写入租户
如果特定租户有大量写入负载,则可能会导致共享集合发生争用。我们建议您仅将流量最大的租户移动到单独的集合中,以分配 I/O 负载。
处理唯一需求(视图)
如果某个租户需要唯一索引或明显大于其他租户,请使用MongoDB视图:
$match仅过滤该租户的文档。
每个租户一个数据库
在每租户数据库设置中,每个租户都包含自己的数据库。此设置会隔离租户数据,但也会成倍增加集群中的索引数量。当每个租户有许多索引字段时,集群可能会达到 2500-索引上限。
警告
大量索引会在基础集群上产生大量负载,并可能扰乱您的工作负载。为保持集群稳定,请仔细监控索引数量,避免索引总数超过 2500 个。
每个租户一个集合(不推荐)
每个租户的集合策略将每个租户映射到共享数据库中自己的集合。维护每个集合的索引的开销可能会导致:
复制延迟增加
由于资源争用而导致的 OOM 错误
基础层级不稳定
我们不建议对MongoDB Search 使用这种策略,除非租户具有非常可预测的轻量级工作负载。如果您当前使用此策略,迁移到所有租户的一个集合策略。
管理索引字段
请考虑以下建议来管理索引字段:
多租户应用程序通常需要可搜索、排序、分面和筛选的自定义每租户字段。使用typeSet 动态索引路径的子字段,以便MongoDB Search 自动选取新字段,而无需重建完整索引。避免对这些字段进行静态索引,因为这样做会触发重建,从而增加 CPU 压力并消耗高达2 倍索引的磁盘空间。
注意
字段过多的风险
1000如果不同字段超过 个, Lucene索引性能可能会下降。您可以在MongoDB Search指标中监控MongoDB Search索引中唯一字段的数量。如果您需要支持一千个或更多字段,我们建议您执行以下操作:
使用视图将租户子集分离到单独的索引中,以便单个Lucene索引中存在的不同字段数保持在 1,000 以下。
限制单个租户可以添加的字段数量。
如果使用属性模式为每个租户存储不同的密钥,我们建议您执行以下操作:
使用视图展平嵌入式文档大量。
在视图上创建MongoDB搜索索引,并将其存储在磁盘上。
如果不展平文档大量,则需要使用 embeddedDocuments操作符查询(类似于 $elemMatch)来查询属性,这比查询展平结构的性能要低。