會員配對Compatible with Milvus v3.0.2+
概述
成員資格比對會透過檢測標量欄位值是否屬於呼叫方提供的集合,來篩選實體。客戶端會建立一個緊湊的二進位成員資格篩選器資料塊,並將其作為表達式範本值傳遞。
此機制取代了龐大的 `IN [...] ` 清單。IN 篩選器會將每個成員序列化至請求中,因此若成員集規模過大,可能會超過代理的 gRPC 接收限制。透過 `membership_match`,代理會驗證該 blob 並將其嵌入查詢計畫中,而無需重新建構成員清單。
成員匹配工作流程:客戶端建立可重複使用的 blob,代理伺服器進行驗證,而 QueryNodes 則負責評估。
此圖示說明了固定的分工模式:客戶端僅需建立一次 blob 並在各請求間重複使用;代理伺服器會驗證封套與大小限制,並將 blob 原樣嵌入查詢計畫中;而 QueryNodes 則會根據原始資料或索引來評估該計畫。
該運算子具有兩種編碼方式,其正確性保證各不相同:
- Bloom(
type=bloom):近似成員資格。可能出現假陽性;但若篩選器有效,則不預期出現假陰性。典型用途:針對大量受眾或排除篩選器,且可接受偶發性假陽性。 - Roaring(
type=roaring):針對整數值的精確成員資格。典型用途:存取控制清單(ACL)、租戶可見性、合規性篩選器,以及依 ID 集合進行刪除的操作。
請勿將布隆(Bloom)成員資格用於授權決策或具破壞性的刪除操作。誤判(false positive)可能導致未包含在目標集合中的實體被暴露,或被誤刪除。
運作檢查清單
- 確認目標欄位類型,並根據該值域建構 BLOB。
- 僅在可接受誤報的情況下才選擇布隆演算法。
- 針對 ACL、合規性、租戶隔離及刪除操作,請選用 Roaring 演算法。
- 大型 BLOB 應僅建立一次並重複使用;請勿在每次請求中重新建立。
- 以序列化位元組為單位預估 Blob 大小,而非成員數量。
- 在混合版本叢集中,請先完成滾動升級(優先處理代理),再啟用「
membership_match」。 - 在啟用破壞性工作流程之前,請先在非生產環境的集合上測試該表達式。
表達式語法
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 時,伺服器會偵測該標頭。若明確指定的type 與 blob 標頭衝突,將被視為請求錯誤而遭拒絕;未知或截斷的標頭則會導致「失敗即關閉」狀態。
選擇編碼
當可接受誤報時,請使用 Bloom。Bloom 支援有號整數欄位(INT8/16/32/64)、VARCHAR欄位以及JSON路徑。Bloom 篩選器並非精確的允許清單。
fpr (誤報率)是指當某個值從未被插入時,布隆濾波器仍將該值報告為成員的機率:fpr=0.005 表示大約每 200 個非成員值中,就有 1 個被錯誤地報告為存在。較低的誤報率會產生較大的二進位大塊資料。
當每個匹配都必須完全精確時,請使用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 仍是最簡單的選項。伺服器不會在IN、Bloom 和Roaring 之間進行選擇。選擇編碼是呼叫方的責任。
選擇編碼時:
- 確定工作負載是否能容忍誤報。若無法容忍,則絕不能選用Bloom編碼。
- 若需精確成員資格,請使用帶有有號整數欄位的Roaring。密集或聚類的值壓縮後可產生最小的 blob。
- 檢查大小限制。均勻隨機的 64 位元值可能會超過 262,144 的高容器上限或大小限制。若確實超過,請使用符合請求限制的傳統IN清單。
- 若需對非整數欄位進行精確成員資格比對,請使用IN。此類欄位目前沒有專用的精確成員資格編碼。
RESTful HTTP API 不支援成員資格比對。HTTP 表達式範本參數僅接受 bool、number、string 和 array 值;不存在用於 blob 的二進位bytes 類型。請使用基於 gRPC 的 SDK 來傳遞成員資格 blob。
SDK 請求模式
以下程式碼片段著重於建立成員資格 Blob 並將其作為篩選模板參數傳遞。這些範例假設集合與欄位已存在且已載入、SDK 客戶端已連線,且查詢向量、成員清單及其他被引用的變數均已定義。本文省略了集合設定與完整的應用程式骨架。
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。在發出刪除指令前,請先檢視成員集合與集合欄位類型。請勿使用 Bloom:近似成員關係並非安全的刪除謂詞,且 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 ` 的刪除操作會移除集合內的實體,使用 `not membership_match ` 的刪除操作會移除集合外的實體,而 `NULL` 實體在兩種極性下均會保留。
- 無論採用何種極性,字段值為 NULL 的實體絕不會被刪除。
- 空位圖不會刪除任何內容。
not membership_match(<field>, {<empty>}, type=roaring)會刪除所有欄位值非 NULL 的實體,即精確的補集,這在設計上可能是一個很大的集合。 not membership_match這是針對非 NULL 實體的成員集合的精確補集,而非近似值。
對於以索引專用方式載入且不含原始標量資料的封閉區段,當索引支援時,評估會使用有界反向查詢。當既無原始欄位資料也無符合條件的索引時,請求會以「FieldNotLoaded 」失敗,而非返回未過濾的實體。
運作限制
| 設定 | 預設值 | 可配置 | 所限制的範圍 |
|---|---|---|---|
proxy.maxMembershipFilterSize | 64 MiB | milvus.yaml 代理 | 一個 Blob 內容體 |
proxy.maxMembershipFilterPlanSize | 128 MiB | milvus.yaml 代理 | 每次請求的聚合值 |
proxy.grpc.serverMaxRecvSize | 128 MiB | milvus.yaml 代理 | 整個請求 |
| Roaring 高容器上限 | 262,144 | 在程式碼中修正 | 每個過濾器 |
| Roaring 解碼後記憶體估算值 | 64 MiB | 在程式碼中固定 | 每個濾波器 |
| MBF1 格式上限 | 128 MiB | 在程式碼中固定 | blob 格式上限 |
Bloomfpr | 0.005 | 客戶端建構器 | 假陽性目標 |
proxy.* 設定屬於 Milvus 伺服器配置:這些設定由叢集管理員在milvus.yaml 的proxy 區段中設定,且無法在過濾時針對個別請求或表達式進行調整。Roaring准入機制與 MBF1 格式上限在實作中已固定,無法在任何處進行配置。fpr 列是唯一的客戶端控制項:其值在建立 blob 時選定,有效範圍為 0.0001 至 0.05。
在預設的 fpr 值下,blob 主體最多可容納約 4,860 萬個 INT64 成員,且允許在頂層附加固定的 32 位元組封套標頭。 請求層級的總配額涵蓋了包含成員的計畫之總序列化大小,加上 Roaring 篩選器的總預估解碼大小,而 HybridSearch 子請求與評分篩選器則共享此配額。Bloom 本體的大小為 2 的冪次:當成員數剛超過某個層級的界限時,Blob 大小會加倍;而稍高的 fpr 通常能讓其符合下一級的限制。 當代理伺服器拒絕過大的布魯姆資料塊時,錯誤訊息會建議一個符合要求的fpr 。
- 成員資格表達式的結果不會在不同請求之間進行快取。
- 混合版本叢集會以「失敗」狀態關閉:執行舊版程式碼的 QueryNodes 會拒絕包含成員資格計畫節點的計畫。請先升級代理伺服器,然後在啟用操作員之前完成滾動升級。
- 伺服器日誌會對 Blob 位元組進行遮蔽處理。表達式文字僅包含範本名稱;二進位資料則透過範本值以帶外方式傳輸。