• 文章
  • 如何在 Qdrant 中实现多租户和自定义分片
返回向量搜索手册

如何在 Qdrant 中实现多租户和自定义分片

David Myriel

·

2024年2月6日

How to Implement Multitenancy and Custom Sharding in Qdrant

扩展您的机器学习设置:Qdrant 中多租户和自定义分片的力量

我们每天都在 Discord 支持频道 上看到有关多租户分布式部署的话题。这表明许多用户正寻求将 Qdrant 与其机器学习设置的其他部分一同扩展。

无论您是构建银行欺诈检测系统、用于电商的 RAG,还是为联邦政府提供服务——您都需要利用多租户架构来扩展您的产品。在 SaaS 和企业应用领域,这种设置是常态。它将显著提升您的应用性能并降低托管成本。

Qdrant 的多租户与自定义分片

我们为此开发了两个主要功能。您现在可以扩展单个 Qdrant 集群并支持全球所有客户。多租户模式下,每个客户的数据完全隔离,只能由其自己访问。有时,如果数据对位置敏感,Qdrant 还提供了按区域或其他标准划分集群的选项,以进一步保护客户访问。这称为自定义分片

结合这两个功能将产生一种高效分区的架构,进一步利用了单个 Qdrant 集群的便利性。本文将简要解释其优势,并展示如何开始使用这两个功能。

一个集合,多个租户

使用 Qdrant 时,您可以将所有数据批量写入到单个集合中,然后通过向量的 payload(载荷)对其进行分区。这意味着所有用户都在利用单个 Qdrant 集群的能力,但他们的数据在集合内仍然是隔离的。让我们看一个包含两个租户的集合:

图 1: 每个向量都被分配了一个特定的 payload,表示它属于哪个租户。这就是大量不同租户如何共享一个 Qdrant 集合的方式。Qdrant 多租户

Qdrant 旨在擅长在单个集合中处理大量租户。只有当您的数据不均匀或用户向量是由不同的嵌入模型创建时,才应该创建多个集合。创建过多集合可能会导致资源开销并产生依赖性。这会增加成本并影响整体性能。

分片您的数据库

使用 Qdrant,您还可以为每个向量单独指定一个分片。如果您想控制数据在集群中的存放位置,此功能非常有用。例如,一组向量可以分配到其自己节点上的一个分片,而另一组则可以位于完全不同的节点上。

在向量搜索过程中,您的操作将只触及实际需要的分片子集。在大规模部署中,这可以显著提高那些不需要扫描整个集合的操作的性能

这也适用于反向操作。无论何时您搜索内容,都可以指定一个或多个分片,Qdrant 将知道在哪里找到它们。它将避免向集群中的所有机器请求结果。这将最大限度地减少开销并最大化性能。

常见用例

此功能的一个明确用例是管理多租户集合,其中每个租户(无论是用户还是组织)都被假定为独立的,因此他们的数据可以存储在单独的分片中。分片解决了基于区域的数据放置问题,某些数据需要保留在特定位置。然而,要做到这一点,您需要在节点之间移动分片

图 2: 用户可以在同一集合内批量写入和查询与其相关的分片。区域分片有助于避免跨大陆流量。Qdrant 多租户

自定义分片还为您提供了对其他用例的精确控制。基于时间的数据放置意味着数据流可以索引代表最新更新的分片。如果您按日期组织分片,则可以很好地控制检索数据的时效性。这对于社交媒体平台很重要,因为它们高度依赖时间敏感数据。

在我进一步讲解之前……我的用户数据有多安全?

Qdrant 在设计上提供了三个级别的隔离。我们最初引入了基于集合的隔离,但您的扩展设置必须超越这一级别。在此场景中,您将利用基于 payload(载荷)的隔离(来自多租户)和基于资源的隔离(来自分片)。最终目标是拥有一个单一集合,您可以在其中更精确地操作和自定义集群内部的分片放置,并避免任何类型的开销。下图显示了您的数据在两层隔离安排中的布局。

图 3: 用户可以基于两个过滤器查询集合:group_id 和单独的 shard_key_selector。这为您的数据提供了两个额外的隔离级别。Qdrant 多租户

为单个集合创建自定义分片

创建集合时,您需要配置用户自定义分片。这使您可以控制数据的分片放置位置,以便操作仅触及实际需要的分片子集。在大型集群中,这可以显著提高操作性能,因为您无需遍历整个集合来检索数据。

client.create_collection(
    collection_name="{tenant_data}",
    shard_number=2,
    sharding_method=models.ShardingMethod.CUSTOM,
    # ... other collection parameters
)
client.create_shard_key("{tenant_data}", "canada")
client.create_shard_key("{tenant_data}", "germany")

在此示例中,您的集群分布在德国和加拿大。在国际数据传输方面,加拿大和德国的法律有所不同。假设您正在创建一个支持医疗保健行业的 RAG 应用。出于合规目的,您的加拿大客户数据必须与您的德国客户数据明确分开。

尽管属于同一集合,每个分片的数据与其他分片是隔离的,并且可以按此方式检索。有关分片和检索的更多示例,请查阅分布式部署文档和Qdrant 客户端规范

为用户配置多租户设置

让我们继续并开始添加数据。当您将向量批量写入新集合时,可以为每个向量添加一个 group_id 字段。如果您这样做,Qdrant 将为每个向量分配到其相应的组。

此外,现在可以将每个向量分配到一个分片。您可以为每个向量指定 shard_key_selector。在此示例中,您将属于 tenant_1 的数据批量写入加拿大区域。

client.upsert(
    collection_name="{tenant_data}",
    points=[
        models.PointStruct(
            id=1,
            payload={"group_id": "tenant_1"},
            vector=[0.9, 0.1, 0.1], 
        ),
        models.PointStruct(
            id=2,
            payload={"group_id": "tenant_1"},
            vector=[0.1, 0.9, 0.1],
        ),
    ],
    shard_key_selector="canada",
)

请记住,每个 group_id 的数据是隔离的。在下面的示例中,tenant_1 的向量与 tenant_2 的向量是分开存放的。第一个租户将能够访问他们在集群加拿大区域的数据。然而,如下所示,tenant_2 可能只能检索托管在德国的信息。

client.upsert(
    collection_name="{tenant_data}",
    points=[
        models.PointStruct(
            id=3,
            payload={"group_id": "tenant_2"},
            vector=[0.1, 0.1, 0.9],
        ),
    ],
    shard_key_selector="germany",
)

通过过滤器检索数据

当您指定数据检索条件时,访问控制设置就完成了。搜索向量时,您需要使用 query_filtergroup_id 来过滤每个用户的向量。

client.search(
    collection_name="{tenant_data}",
    query_filter=models.Filter(
        must=[
            models.FieldCondition(
                key="group_id",
                match=models.MatchValue(
                    value="tenant_1",
                ),
            ),
        ]
    ),
    query_vector=[0.1, 0.1, 0.9],
    limit=10,
)

性能考量

如果您以这种方式添加大量数据,索引速度可能会成为瓶颈,因为每个用户的向量都会被索引到同一个集合中。为了避免此瓶颈,可以考虑绕过整个集合的全局向量索引构建,而仅为单个组构建索引。

通过采用此策略,Qdrant 将为每个用户独立地索引向量,从而显著加快处理速度。

要实现此方法,您应该

  1. 将 HNSW 配置中的 payload_m 设置为一个非零值,例如 16。
  2. 将 hnsw 配置中的 m 设置为 0。这将禁用为整个集合构建全局索引。
from qdrant_client import QdrantClient, models

client = QdrantClient("localhost", port=6333)

client.create_collection(
    collection_name="{tenant_data}",
    vectors_config=models.VectorParams(size=768, distance=models.Distance.COSINE),
    hnsw_config=models.HnswConfigDiff(
        payload_m=16,
        m=0,
    ),
)
  1. group_id 字段创建关键字 payload 索引。
client.create_payload_index(
    collection_name="{tenant_data}",
    field_name="group_id",
    field_schema=models.PayloadSchemaType.KEYWORD,
)

注意:请记住,全局请求(不带 group_id 过滤器)会较慢,因为它们需要扫描所有组才能找到最近邻。

探索 Qdrant 中的多租户和自定义分片,实现可扩展解决方案

Qdrant 已准备好为您的机器学习项目支持大规模架构。如果您想了解我们的向量数据库是否适合您,请尝试快速入门教程或阅读我们的文档和教程

要启动免费的 Qdrant 实例,请注册Qdrant Cloud - 无需绑定信用卡。

在我们的Discord社区中获得支持或分享想法。在这里,我们讨论向量搜索理论,发布示例和演示,并讨论向量数据库设置。

此页面有用吗?

感谢您的反馈!🙏

我们很抱歉听到此消息。😔 您可以在 GitHub 上编辑此页面,或创建一个 GitHub issue。