会员配对Compatible with Milvus v3.0.2+

概述

成员匹配通过测试标量字段值是否属于调用方提供的集合来过滤实体。客户端构建一个紧凑的二进制成员过滤块,并将其作为表达式模板值传递。

它取代了庞大的IN [...] 列表。IN 过滤器会将每个成员序列化到请求中,因此成员集如果足够大,可能会超过代理的gRPC接收限制。而使用membership_match 时,代理会验证该blob并将其嵌入查询计划中,而无需重新构建成员列表。

Membership Match workflow: the client builds a reusable blob, the proxy validates it, and QueryNodes evaluate it. 成员匹配工作流:客户端构建一个可重用的 blob,代理对其进行验证,而 QueryNodes 则对其进行评估。

该图展示了固定的分工机制:客户端仅需构建一次 blob 即可在多个请求中复用;代理会验证封装和大小限制,并将 blob 原样嵌入查询计划;而 QueryNodes 则根据原始数据或索引对该计划进行评估。

该操作符提供两种编码方式,其正确性保证各不相同:

  • 布卢姆(type=bloom ):近似成员资格。可能出现误报;对于有效的过滤器,不预期出现漏报。典型用途:针对海量受众或排除过滤器,其中偶尔出现的误报是可以接受的。
  • Roaring(type=roaring ):针对整数值提供精确成员资格判定。典型用途:访问控制列表(ACL)、租户可见性、合规性过滤器以及按 ID 集合删除的操作。

请勿在授权决策或破坏性删除操作中使用布卢姆(Bloom)成员资格。误报可能会导致未包含在目标集合中的实体被暴露,或被误删。

操作检查清单

  • 确认目标字段类型,并基于该值域构建 BLOB。
  • 仅在可接受误报的情况下才选择布隆算法。
  • 对于 ACL、合规性、租户隔离和删除操作,请选择 Roaring。
  • 大型 Blob 应构建一次并重复使用;请勿在每次请求中重新构建。
  • 按序列化字节数(而非成员数量)规划 Blob 大小。
  • 在混合版本集群上,请先完成代理的滚动升级,然后再启用membership_match 。
  • 在启用破坏性工作流之前,请先在非生产环境Collection上测试该表达式。

表达式语法

membership_match(<field>, {<template>}, type=bloom)
membership_match(<field>, {<template>}, type=roaring)
membership_match(<field>, {<template>})
not membership_match(<field>, {<template>}, type=bloom)

第二个参数是模板占位符。SDK 会将占位符值作为 protobufbytes 提供,其中包含客户端构建的过滤 Blob。在生产环境表达式中请显式保留type ,以便在代码和日志中清晰显示预期的正确性契约。

type 参数是可选的。每个blob都携带一个自描述的魔术头(Bloom为MBF1 ,Roaring为MRB1 ),当省略type 时,服务器会嗅探该头。与blob头冲突的显式type 将被视为请求错误而拒绝,未知或截断的头将导致失败关闭状态。

选择编码

当可接受误报时,请使用 Bloom。Bloom 支持有符号整数字段(INT8/16/32/64)、VARCHAR字段以及JSON路径。Bloom 过滤器并非精确的允许列表。

fpr (误报率)是指当某个值从未被插入时,布隆过滤器将其报告为成员的概率:fpr=0.005 意味着大约每 200 个非成员值中,就有一个被错误地报告为存在。更低的误报率会导致 Blob 体积增大。

当每个匹配都必须完全精确时,请使用Roaring。Roaring仅接受有符号整数字段;在序列化之前,成员会被扩展为INT64,因此 INT8 类型的 -1 和 INT64 类型的 -1 属于同一个成员。请勿将其用于 UUID 或其他字符串字段。

维度Bloom(type=bloom)Roaring(type=roaring)
精度近似;可能出现假阳性;无假阴性精确;无假阳性,无假阴性
字段INT8/16/32/64、VARCHAR、JSON 路径仅限有符号 INT8/16/32/64
BLOB 大小成员数量和 FPR;当 FPR 为 0.005 时,每个成员约占 11 位;二进制幂结构值分布;稠密集合可压缩至远小于原始大小的体积;随机 64 位值会达到 262,144 的容器上限
删除在解析时被拒绝允许
典型用途受众和排除过滤器访问控制列表(ACL)、合规性、租户隔离、按 ID 集合删除

JSON路径是严格类型化的,按行处理:JSON字符串被视为VARCHAR值,JSON整数被视为INT64值,而JSON双精度值绝不会与整数成员匹配,这与精确的IN 不同,在后者中 5.0 等于 5. 如果您的写入程序将整数浮点编码,请在写入时对其进行规范化处理,或者使用类型化的标量字段。

无论采用哪种编码方式,都应基于与目标字段相同的值域构建成员集。 请勿基于字符串"10086" 构建布隆过滤器,并将其应用于INT64 字段。BLOB 头部记录了插入的值域,如果 BLOB 的值域错误,代理会返回参数错误并拒绝该 BLOB,而不是默默地不进行匹配。请在不同请求中复用已构建的 BLOB,而不是为每个查询重新构建。

对于成员数在几千个或更少的集合,普通的 `IN ` 仍是 simplest 选项。服务器不会在`IN`、`Bloom` 和`Roaring` 之间进行选择。选择编码是调用方的责任。

选择编码时:

  1. 确定工作负载能否容忍误报。如果不能,则绝不能选择Bloom编码。
  2. 若需精确成员资格判定,请使用带符号整数字段的Roaring 编码。密集或簇状的值可压缩为最小的 blob。
  3. 检查大小限制。均匀随机生成的 64 位值可能会超过 262,144 的高容器上限或大小限制。如果超出,请使用符合请求限制的传统IN列表。
  4. 对于非整数字段的精确成员资格,请使用IN。这些字段没有精确成员资格编码。

RESTful HTTP API 不支持成员资格匹配。HTTP 表达式模板参数仅接受 bool、number、string 和 array 值;不存在用于 blob 的二进制bytes 类型。请使用基于 gRPC 的 SDK 传递成员资格 blob。

SDK 请求模式

以下代码片段重点介绍如何构建成员资格 blob 并将它们作为过滤器模板参数传递。这些示例假设 Collection 和字段已存在并已加载,SDK 客户端已连接,且查询向量、成员列表及其他引用的变量均已定义。本文省略了 Collection 的设置和完整的应用程序框架。

from pymilvus import MilvusClient, build_bloom_filter, build_roaring_bitmap

client = MilvusClient(uri="http://localhost:19530")
bloom_blob = build_bloom_filter(["creator-a", "creator-b"], fpr=0.005)
client.search(collection_name="posts", data=[query_vector], anns_field="embedding", limit=10,
              filter="membership_match(creator_id, {members}, type=bloom)",
              filter_params={"members": bloom_blob})

roaring_blob = build_roaring_bitmap([1001, 1002, 1003])
client.query(collection_name="docs", filter="membership_match(user_id, {members}, type=roaring)",
             filter_params={"members": roaring_blob}, output_fields=["user_id", "title"])
bloomBlob, err := milvusclient.NewBloomFilterBlob([]int64{1001, 1002}, fpr)
if err != nil { return err }
search := milvusclient.NewSearchOption("posts", topK, vectors).
    WithFilter("membership_match(creator_id, {members}, type=bloom)").
    WithTemplateParam("members", bloomBlob)

roaringBlob, err := milvusclient.NewRoaringBitmapBlob([]int64{1001, 1002})
if err != nil { return err }
query := milvusclient.NewQueryOption("docs").
    WithFilter("membership_match(user_id, {members}, type=roaring)").
    WithTemplateParam("members", roaringBlob)
byte[] bloomBlob = BloomFilterUtils.buildBloomFilter(userIds, BloomFilterUtils.DEFAULT_FPR);
QueryReq request = QueryReq.builder().collectionName("docs")
    .filter("membership_match(user_id, {members}, type=bloom)")
    .filterTemplateValues(Collections.singletonMap("members", bloomBlob)).build();
byte[] roaringBlob = RoaringBitmapUtils.buildRoaringBitmap(userIds);
const bloomBlob = buildBloomFilter(userIds);
await client.query({ collection_name: 'docs',
  filter: 'membership_match(user_id, {members}, type=bloom)',
  exprValues: { members: bloomBlob } });
const roaringBlob = buildRoaringBitmap(integerIds);

使用 Roaring 进行删除

在删除由外部整数 ID 集合选中的实体时,请使用精确的RoaringBlob。在执行删除操作前,请检查成员集和 Collection 字段类型。请勿使用 Bloom:近似成员关系并非安全的删除谓词,且在解析时,Delete 表达式会拒绝 Bloom 形式。

roaring_blob = build_roaring_bitmap([1001, 1002, 1003])
client.delete(collection_name="docs", filter="membership_match(id, {members}, type=roaring)",
              filter_params={"members": roaring_blob})
roaringBlob, err := milvusclient.NewRoaringBitmapBlob([]int64{1001, 1002, 1003})
if err != nil { return err }
_, err = client.Delete(ctx, milvusclient.NewDeleteOption("docs").
    WithExpr("membership_match(id, {members}, type=roaring)").
    WithTemplateParam("members", roaringBlob))
byte[] roaringBlob = RoaringBitmapUtils.buildRoaringBitmap(userIds);
DeleteReq request = DeleteReq.builder().collectionName("docs")
    .filter("membership_match(id, {members}, type=roaring)")
    .filterTemplateValues(Collections.singletonMap("members", roaringBlob)).build();
const roaringBlob = buildRoaringBitmap(integerIds);
await client.delete({ collection_name: 'docs',
  filter: 'membership_match(id, {members}, type=roaring)',
  exprValues: { members: roaringBlob } });

删除语义遵循精确的三值匹配规则:

字段值membership_match(…)非 membership_match(…)
NULL无匹配 (NULL)无匹配项 (NULL)
在成员集中匹配无匹配项
不在集合中无匹配项匹配

该表按行解读如下:使用membership_match 的Delete操作会删除集合内的实体,使用not membership_match 的Delete操作会删除集合外的实体,而NULL实体在两种极性下均不会被删除。

  • 无论采用哪种极性,字段值为 NULL 的实体都不会被删除。
  • 空位图不会删除任何内容。not membership_match(<field>, {<empty>}, type=roaring) 会删除所有字段值不为 NULL 的实体,即精确补集,根据设计,这可能是一个很大的集合。
  • not membership_match 是成员集在非 NULL 实体上的精确补集,而非近似值。

对于以索引方式加载且不包含原始标量数据的密封段,当索引支持时,评估将使用有界反向查找。当既没有原始字段数据也没有符合条件的索引时,请求将返回FieldNotLoaded 并失败,而不是返回未过滤的实体。

操作限制

设置默认值可配置限制对象
proxy.maxMembershipFilterSize64 MiBmilvus.yaml 代理一个 blob 主体
proxy.maxMembershipFilterPlanSize128 MiBmilvus.yaml 代理每次请求的聚合值
proxy.grpc.serverMaxRecvSize128 MiBmilvus.yaml 代理整个请求
Roaring 高容器上限262,144在代码中已修复每个过滤器
Roaring解码内存估计值64 MiB在代码中固定每个滤波器
MBF1 格式上限128 MiB在代码中固定blob 格式上限
布鲁姆fpr0.005客户端构建器假阳性阈值

proxy.* 设置属于Milvus服务器配置:这些设置由集群管理员在milvus.yaml 的proxy 部分中进行配置,无法在过滤时针对单个请求或单个表达式进行调整。Roaring准入和 MBF1 格式上限在实现中是固定的,无法在任何地方进行配置。fpr 行是唯一的客户端控制项:它在构建 blob 时被选定,允许的范围为 0.0001 到 0.05。

在默认 FPR 条件下,BLOB 主体最多可容纳约 4860 万个 INT64 成员,此外还允许附加固定的 32 字节信封头。 请求级配额涵盖包含成员信息的计划的总序列化大小,以及 Roaring 过滤器的总估计解码大小,HybridSearch 子请求和评分器过滤器共享该配额。Bloom 主体大小为 2 的幂:当成员数刚超过一个层级边界时,Blob 大小会翻倍;而略高的 fpr 通常可适配下一级层级。 当代理拒绝超大尺寸的布卢姆数据块时,错误信息会建议一个符合要求的fpr 。

  • 成员资格表达式的结果不会在不同请求之间进行缓存。
  • 混合版本集群会以失败告终:运行旧版代码的 QueryNodes 会拒绝包含成员资格计划节点的计划。请先升级代理,然后在启用操作符之前完成滚动升级。
  • 服务器日志会屏蔽 Blob 字节。表达式文本仅包含模板名称;二进制有效载荷通过模板值以带外方式传输。

翻译自DeepL

想要更快、更简单、更好用的 Milvus SaaS服务 ?

Zilliz Cloud是基于Milvus的全托管向量数据库,拥有更高性能,更易扩展,以及卓越性价比

免费试用 Zilliz Cloud
反馈

此页对您是否有帮助?