Utilizar a Chave de Partição

A Chave de partição é uma solução de otimização da pesquisa baseada em partições. Ao designar um campo escalar específico como Chave de Partição e ao especificar condições de filtragem com base na Chave de Partição durante a pesquisa, o âmbito da pesquisa pode ser reduzido a várias partições, melhorando assim a eficiência da pesquisa. Este artigo apresentará como utilizar a Chave de partição e considerações relacionadas.

Visão geral

No Milvus, pode utilizar partições para implementar a segregação de dados e melhorar o desempenho da pesquisa, restringindo o âmbito da pesquisa a partições específicas. Se optar por gerir as partições manualmente, pode criar um máximo de 1024 partições numa coleção e inserir entidades nestas partições com base numa regra específica para que possa limitar o âmbito da pesquisa restringindo as pesquisas dentro de um número específico de partições.

O Milvus introduz a Chave de Partição para que possa reutilizar partições na segregação de dados para ultrapassar o limite do número de partições que pode criar numa coleção. Ao criar uma coleção, pode utilizar um campo escalar como chave de partição. Quando a coleção estiver pronta, o Milvus cria o número especificado de partições dentro da coleção. Ao receber uma entidade inserida, Milvus calcula um valor de hash usando o valor da chave de partição da entidade, executa uma operação de módulo com base no valor de hash e na propriedade partitions_num da coleção para obter o ID da partição de destino, e armazena a entidade na partição de destino.

Partition Vs Partition Key Partição Vs Chave de Partição

A figura seguinte ilustra a forma como o Milvus processa os pedidos de pesquisa numa coleção com ou sem a funcionalidade Chave de Partição activada.

  • Se a Chave de Partição estiver desactivada, o Milvus procura as entidades que são mais semelhantes ao vetor de consulta dentro da coleção. Pode limitar o âmbito da pesquisa se souber qual a partição que contém os resultados mais relevantes.

  • Se a Chave de Partição estiver activada, o Milvus determina o âmbito da pesquisa com base no valor da Chave de Partição especificado num filtro de pesquisa e analisa apenas as entidades dentro das partições que correspondem.

With And Without Partition Key Com e sem chave de partição

Usar chave de partição

Para usar a Chave de Partição, é necessário

Definir chave de partição

Para designar um campo escalar como a Chave de Partição, é necessário definir o atributo is_partition_key como true quando adicionar o campo escalar.

Quando você define um campo escalar como a Chave de partição, os valores do campo não podem estar vazios ou nulos.

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
                }
            }
        ]
    }'

Definir números de partição

Quando se designa um campo escalar numa coleção como chave de partição, Milvus cria automaticamente 16 partições na coleção. Ao receber uma entidade, o Milvus escolhe uma partição com base no valor da Chave de Partição desta entidade e armazena a entidade na partição, resultando em algumas ou todas as partições contendo entidades com diferentes valores de Chave de Partição.

Também é possível determinar o número de partições a criar juntamente com a coleção. Isso é válido apenas se você tiver um campo escalar designado como a Chave de partição.

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" \
-d "{
    \"collectionName\": \"my_collection\",
    \"schema\": $schema,
    \"params\": $params
}"

Criar condição de filtragem

Ao realizar pesquisas ANN em uma coleção com o recurso Chave de partição habilitado, é necessário incluir uma expressão de filtragem envolvendo a Chave de partição na solicitação de pesquisa. Na expressão de filtragem, pode restringir o valor da Chave de partição dentro de um intervalo específico para que o Milvus restrinja o âmbito da pesquisa dentro das partições correspondentes.

Ao efetuar operações de eliminação, é aconselhável incluir uma expressão de filtragem que especifique uma única chave de partição para conseguir uma eliminação mais eficiente. Esta abordagem limita a operação de eliminação a uma partição específica, reduzindo a amplificação da escrita durante a compactação e conservando os recursos para compactação e indexação.

Os exemplos a seguir demonstram a filtragem baseada em chave de partição com base em um valor de chave de partição específico e um conjunto de valores de chave de partição.

# 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>'

É necessário substituir partition_key pelo nome do campo que é designado como chave de partição.

Usar o isolamento da chave de partição

No cenário multilocatário, pode designar o campo escalar relacionado com as identidades dos locatários como a chave de partição e criar um filtro com base num valor específico neste campo escalar. Para melhorar ainda mais o desempenho da pesquisa em cenários semelhantes, o Milvus introduz a funcionalidade Partition Key Isolation.

Partition Key Isolation Isolamento da chave de partição

Como mostra a figura acima, Milvus agrupa as entidades com base no valor da chave de partição e cria um índice separado para cada um desses grupos. Ao receber um pedido de pesquisa, o Milvus localiza o índice com base no valor da Chave de Partição especificado na condição de filtragem e restringe o âmbito da pesquisa às entidades incluídas no índice, evitando assim a pesquisa de entidades irrelevantes durante a pesquisa e melhorando consideravelmente o desempenho da pesquisa.

Uma vez ativado o Isolamento da Chave de Partição, é necessário incluir apenas um valor específico no filtro baseado na Chave de Partição para que o Milvus possa restringir o âmbito da pesquisa dentro das entidades incluídas no índice que correspondem.

Atualmente, a funcionalidade Isolamento da Chave de Partição aplica-se apenas a pesquisas com o tipo de índice definido como HNSW.

Ativar o isolamento da chave de partição

Os exemplos de código a seguir demonstram como habilitar o Isolamento de chave de partição.

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" \
-d "{
    \"collectionName\": \"my_collection\",
    \"schema\": $schema,
    \"params\": $params
}"

Depois de ativar o isolamento da chave de partição, pode ainda definir a chave de partição e o número de partições, conforme descrito em Definir números de partição. Observe que o filtro baseado na Chave de partição deve incluir apenas um valor específico de Chave de partição.