パーティション・キーの使用

パーティション・キーはパーティションに基づく検索最適化ソリューションです。特定のスカラー・フィールドをパーティション・キーとして指定し、検索時にパーティション・キーに基づくフィルタリング条件を指定することで、検索範囲を複数のパーティションに絞り込み、検索効率を向上させることができる。本稿では、パーティション・キーの使い方と注意点について紹介する。

概要

Milvusでは、パーティションを使ってデータの分離を行い、検索範囲を特定のパーティションに限定することで検索性能を向上させることができます。パーティションを手動で管理する場合、コレクション内に最大1,024個のパーティションを作成し、特定のルールに基づいてこれらのパーティションにエンティティを挿入することで、特定の数のパーティション内に検索を制限して検索範囲を絞り込むことができます。

Milvusは、コレクションに作成できるパーティション数の制限を克服するために、データ分離でパーティションを再利用するためのパーティションキーを導入しています。コレクションを作成する際、パーティション・キーとしてスカラー・フィールドを使用することができます。コレクションの準備ができると、Milvusはコレクション内に指定された数のパーティションを作成します。挿入されたエンティティを受け取ると、Milvusはエンティティのパーティションキー値を使用してハッシュ値を計算し、ハッシュ値とコレクションのpartitions_num プロパティに基づいてモジュロ演算を実行してターゲットパーティションIDを取得し、ターゲットパーティションにエンティティを格納します。

Partition Vs Partition Key パーティション対パーティション・キー

次の図は、Milvusがパーティション・キー機能を有効にしている場合と無効にしていない場合で、コレクション内の検索要求をどのように処理するかを示しています。

  • パーティション・キーが無効の場合、Milvusはコレクション内のクエリ・ベクタに最も類似したエンティティを検索します。どのパーティションに最も関連性の高い結果が含まれているかを知っていれば、検索範囲を狭めることができます。

  • パーティション・キーが有効な場合、Milvusは検索フィルターで指定されたパーティション・キーの値に基づいて検索範囲を決定し、一致するパーティション内のエンティティだけをスキャンします。

With And Without Partition Key パーティションキーの有無

パーティション・キーの使用

パーティション・キーを使用するには、以下の手順が必要です。

パーティション・キーの設定

スカラー・フィールドをパーティション・キーとして指定するには、スカラー・フィールドを追加するときに、そのis_partition_key 属性をtrue に設定する必要があります。

スカラー・フィールドをパーティション・キーに設定する場合、フィールドの値を空やNULLにすることはできません。

from pymilvus import (
    MilvusClient, DataType
)

client = MilvusClient(
    uri="http://localhost:19530",
    token="root:Milvus"
)

schema = client.create_schema()

schema.add_field(field_name="id",
    datatype=DataType.INT64,
    is_primary=True)
    
schema.add_field(field_name="vector",
    datatype=DataType.FLOAT_VECTOR,
    dim=5)

# Add the partition key
schema.add_field(
    field_name="my_varchar", 
    datatype=DataType.VARCHAR, 
    max_length=512,
    is_partition_key=True,
)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.common.DataType;
import io.milvus.v2.service.collection.request.AddFieldReq;
import io.milvus.v2.service.collection.request.CreateCollectionReq;

MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
        .uri("http://localhost:19530")
        .token("root:Milvus")
        .build());

// Create schema
CreateCollectionReq.CollectionSchema schema = client.createSchema();

schema.addField(AddFieldReq.builder()
        .fieldName("id")
        .dataType(DataType.Int64)
        .isPrimaryKey(true)
        .build());

schema.addField(AddFieldReq.builder()
        .fieldName("vector")
        .dataType(DataType.FloatVector)
        .dimension(5)
        .build());
        
// Add the partition key
schema.addField(AddFieldReq.builder()
        .fieldName("my_varchar")
        .dataType(DataType.VarChar)
        .maxLength(512)
        .isPartitionKey(true)
        .build());
import (
    "context"
    "fmt"

    "github.com/milvus-io/milvus/client/v2/column"
    "github.com/milvus-io/milvus/client/v2/entity"
    "github.com/milvus-io/milvus/client/v2/index"
    "github.com/milvus-io/milvus/client/v2/milvusclient"
)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

milvusAddr := "localhost:19530"
client, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
    Address: milvusAddr,
})
if err != nil {
    fmt.Println(err.Error())
    // handle error
}
defer client.Close(ctx)

schema := entity.NewSchema().WithDynamicFieldEnabled(false)
schema.WithField(entity.NewField().
    WithName("id").
    WithDataType(entity.FieldTypeInt64).
    WithIsPrimaryKey(true),
).WithField(entity.NewField().
    WithName("my_varchar").
    WithDataType(entity.FieldTypeVarChar).
    WithIsPartitionKey(true).
    WithMaxLength(512),
).WithField(entity.NewField().
    WithName("vector").
    WithDataType(entity.FieldTypeFloatVector).
    WithDim(5),
)
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";

const address = "http://localhost:19530";
const token = "root:Milvus";
const client = new MilvusClient({address, token});

// 3. Create a collection in customized setup mode
// 3.1 Define fields
const fields = [
    {
        name: "my_varchar",
        data_type: DataType.VarChar,
        max_length: 512,
        is_partition_key: true
    }
]
export schema='{
        "autoId": true,
        "enabledDynamicField": false,
        "fields": [
            {
                "fieldName": "id",
                "dataType": "Int64",
                "isPrimary": true
            },
            {
                "fieldName": "vector",
                "dataType": "FloatVector",
                "elementTypeParams": {
                    "dim": "5"
                }
            },
            {
                "fieldName": "my_varchar",
                "dataType": "VarChar",
                "isPartitionKey": true,
                "elementTypeParams": {
                    "max_length": 512
                }
            }
        ]
    }'

パーティション番号の設定

コレクション内のスカラーフィールドをパーティションキーに指定すると、Milvusは自動的にコレクション内に16のパーティションを作成します。Milvusはエンティティを受信すると、このエンティティのパーティションキー値に基づいてパーティションを選択し、そのパーティションにエンティティを格納します。

また、コレクションと一緒に作成するパーティションの数を決定することもできます。これは、パーティション・キーとしてスカラー・フィールドを指定した場合のみ有効です。

client.create_collection(
    collection_name="my_collection",
    schema=schema,
    num_partitions=128
)
import io.milvus.v2.service.collection.request.CreateCollectionReq;

CreateCollectionReq createCollectionReq = CreateCollectionReq.builder()
                .collectionName("my_collection")
                .collectionSchema(schema)
                .numPartitions(128)
                .build();
        client.createCollection(createCollectionReq);
err = client.CreateCollection(ctx,
    milvusclient.NewCreateCollectionOption("my_collection", schema).
        WithNumPartitions(128))
if err != nil {
    fmt.Println(err.Error())
    // handle error
}
await client.create_collection({
    collection_name: "my_collection",
    schema: schema,
    num_partitions: 128
})
export params='{
    "partitionsNum": 128
}'

export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d "{
    \"collectionName\": \"my_collection\",
    \"schema\": $schema,
    \"params\": $params
}"

フィルタリング条件の作成

パーティション・キー機能を有効にしたコレクションでANN検索を行う場合、検索リクエストにパーティション・キーを含むフィルタリング式を含める必要があります。フィルタリング式では、Milvusが対応するパーティション内で検索範囲を制限するように、特定の範囲内でパーティションキーの値を制限することができます。

削除操作を実行する場合、より効率的な削除を実現するために、単一のパーティション・キーを指定するフィルタ式を含めることをお勧めします。このアプローチは、削除操作を特定のパーティションに限定し、コンパクション時の書き込み増幅を減らし、コンパクションとインデックス作成のリソースを節約します。

以下の例は、特定のパーティション・キー値とパーティション・キー値のセットに基づくパーティション・キー・ベース・フィルタリングを示しています。

# Filter based on a single partition key value, or
filter='partition_key == "x" && <other conditions>'

# Filter based on multiple partition key values
filter='partition_key in ["x", "y", "z"] && <other conditions>'
// Filter based on a single partition key value, or
String filter = "partition_key == 'x' && <other conditions>";

// Filter based on multiple partition key values
String filter = "partition_key in ['x', 'y', 'z'] && <other conditions>";
// Filter based on a single partition key value, or
filter = "partition_key == 'x' && <other conditions>"

// Filter based on multiple partition key values
filter = "partition_key in ['x', 'y', 'z'] && <other conditions>"
// Filter based on a single partition key value, or
const filter = 'partition_key == "x" && <other conditions>'

// Filter based on multiple partition key values
const filter = 'partition_key in ["x", "y", "z"] && <other conditions>'
# Filter based on a single partition key value, or
export filter='partition_key == "x" && <other conditions>'

# Filter based on multiple partition key values
export filter='partition_key in ["x", "y", "z"] && <other conditions>'

partition_key 、パーティション・キーとして指定されているフィールド名に置き換える必要があります。

パーティション・キーの分離を使用する

マルチテナントのシナリオでは、テナントIDに関連するスカラー・フィールドをパーティション・キーとして指定し、このスカラー・フィールドの特定の値に基づいてフィルターを作成できます。同様のシナリオにおける検索パフォーマンスをさらに向上させるために、Milvusはパーティションキーアイソレーション機能を導入しています。

Partition Key Isolation パーティションキーの分離

上図のように、Milvusはパーティションキーの値に基づいてエンティティをグループ化し、これらのグループごとに個別のインデックスを作成します。検索要求を受け取ると、Milvusはフィルタリング条件に指定されたパーティションキー値に基づいてインデックスを検索し、インデックスに含まれるエンティティ内で検索範囲を制限します。これにより、検索中に無関係なエンティティをスキャンすることがなくなり、検索パフォーマンスが大幅に向上します。

Partition Key Isolationを有効にすると、Milvusが一致するインデックスに含まれるエンティティ内で検索範囲を制限できるように、Partition Keyベースのフィルタに特定の値を1つだけ含める必要があります。

現在、パーティションキー分離機能は、インデックスタイプが HNSW に設定された検索にのみ適用されます。

パーティション・キー分離の有効化

以下のコード例は、パーティション・キー分離を有効にする方法を示しています。

client.create_collection(
    collection_name="my_collection",
    schema=schema,
    properties={"partitionkey.isolation": True}
)
import io.milvus.v2.service.collection.request.CreateCollectionReq;

Map<String, String> properties = new HashMap<>();
properties.put("partitionkey.isolation", "true");

CreateCollectionReq createCollectionReq = CreateCollectionReq.builder()
        .collectionName("my_collection")
        .collectionSchema(schema)
        .properties(properties)
        .build();
client.createCollection(createCollectionReq);
err = client.CreateCollection(ctx,
    milvusclient.NewCreateCollectionOption("my_collection", schema).
        WithProperty("partitionkey.isolation", true))
if err != nil {
    fmt.Println(err.Error())
    // handle error
}
res = await client.alterCollection({
    collection_name: "my_collection",
    properties: {
        "partitionkey.isolation": true
    }
})
export params='{
    "partitionKeyIsolation": true
}'

export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d "{
    \"collectionName\": \"my_collection\",
    \"schema\": $schema,
    \"params\": $params
}"

パーティション・キー・アイソレーションを有効にした後も、パーティション・ナンバーの設定 で説明するように、パーティション・キーとパーティション数を設定することができます。パーティション・キー・ベース・フィルターには、特定のパーティション・キーの値だけを含める必要があることに注意してください。