Nível de consistência
Como um banco de dados vetorial distribuído, Milvus oferece vários níveis de consistência para garantir que cada nó ou réplica possa acessar os mesmos dados durante as operações de leitura e escrita. Atualmente, os níveis de consistência suportados incluem Strong, Bounded, Eventually e Session, sendo que Bounded é o nível de consistência utilizado por defeito.
Visão geral
Milvus é um sistema que separa o armazenamento e a computação. Neste sistema, os DataNodes são responsáveis pela persistência dos dados e, em última análise, armazenam-nos no armazenamento de objectos distribuído, como o MinIO/S3. Os QueryNodes tratam de tarefas computacionais como a Pesquisa. Estas tarefas envolvem o processamento de dados em lote e de dados em fluxo contínuo. Em termos simples, os dados em lote podem ser entendidos como dados que já foram armazenados no armazenamento de objectos, enquanto os dados de fluxo contínuo se referem a dados que ainda não foram armazenados no armazenamento de objectos. Devido à latência da rede, os QueryNodes muitas vezes não possuem os dados de streaming mais recentes. Sem salvaguardas adicionais, a execução da Pesquisa diretamente nos dados de fluxo contínuo pode resultar na perda de muitos pontos de dados não confirmados, afectando a precisão dos resultados da pesquisa.
O Milvus Commercial Edition é um sistema que separa o armazenamento e a computação. Neste sistema, os DataNodes são responsáveis pela persistência dos dados e, em última análise, armazenam-nos em armazenamento de objectos distribuídos, como o MinIO/S3. Os QueryNodes tratam de tarefas computacionais como a Pesquisa. Estas tarefas envolvem o processamento de dados em lote e de dados em fluxo contínuo. Em termos simples, os dados em lote podem ser entendidos como dados que já foram armazenados no armazenamento de objectos, enquanto os dados de fluxo contínuo se referem a dados que ainda não foram armazenados no armazenamento de objectos. Devido à latência da rede, os QueryNodes muitas vezes não possuem os dados de streaming mais recentes. Sem salvaguardas adicionais, a execução da Pesquisa diretamente nos dados de fluxo contínuo pode resultar na perda de muitos pontos de dados não confirmados, afectando a precisão dos resultados da pesquisa.
Dados em lote e dados de fluxo contínuo
Como mostrado na figura acima, os QueryNodes podem receber dados de streaming e dados em lote simultaneamente após receberem um pedido de pesquisa. No entanto, devido à latência da rede, os dados em fluxo contínuo obtidos pelos QueryNodes podem estar incompletos.
Para resolver este problema, o Milvus marca o tempo de cada registo na fila de dados e insere continuamente marcas de tempo de sincronização na fila de dados. Sempre que um carimbo de data/hora de sincronização (syncTs) é recebido, os QueryNodes definem-no como ServiceTime, o que significa que os QueryNodes podem ver todos os dados anteriores a esse ServiceTime. Com base no ServiceTime, o Milvus pode fornecer carimbos de data/hora de garantia (GuaranteeTs) para satisfazer os diferentes requisitos dos utilizadores em termos de consistência e disponibilidade. Os utilizadores podem informar os QueryNodes da necessidade de incluir dados anteriores a um determinado momento no âmbito da pesquisa, especificando GuaranteeTs nos seus pedidos de pesquisa.
Tempo de serviço e tempo de garantia
Conforme mostrado na figura acima, se GuaranteeTs for menor que ServiceTime, significa que todos os dados antes do ponto de tempo especificado foram totalmente gravados no disco, permitindo que os nós de consulta executem imediatamente a operação de pesquisa. Quando GuaranteeTs é superior a ServiceTime, os QueryNodes têm de esperar até que ServiceTime exceda GuaranteeTs para poderem executar a operação Search.
Os utilizadores precisam de fazer um compromisso entre a precisão da consulta e a latência da consulta. Se os utilizadores tiverem requisitos de consistência elevados e não forem sensíveis à latência da consulta, podem definir GuaranteeTs para um valor tão grande quanto possível; se os utilizadores pretenderem receber rapidamente os resultados da pesquisa e forem mais tolerantes à precisão da consulta, então GuaranteeTs pode ser definido para um valor mais pequeno.
Nível de consistência ilustrado
O Milvus fornece quatro tipos de níveis de consistência com diferentes GuaranteeTs.
Forte
O carimbo de data/hora mais recente é utilizado como GuaranteeTs e os QueryNodes têm de aguardar que o ServiceTime cumpra os GuaranteeTs antes de executarem os pedidos de pesquisa.
Eventual
O GuaranteeTs é definido para um valor extremamente pequeno, como 1, para evitar verificações de consistência, de modo a que os QueryNodes possam executar imediatamente pedidos de Pesquisa em todos os dados do lote.
Staleness limitado
O GuranteeTs é definido para um ponto de tempo anterior ao último carimbo de data/hora para que os QueryNodes executem pesquisas com uma tolerância de determinada perda de dados.
Sessão
O último ponto temporal em que o cliente insere dados é utilizado como GuaranteeTs para que os QueryNodes possam efetuar pesquisas em todos os dados inseridos pelo cliente.
O Milvus utiliza o Bounded Staleness como nível de consistência predefinido. Se o GuaranteeTs não for especificado, o ServiceTime mais recente é utilizado como GuaranteeTs.
Definir o nível de consistência
Pode definir diferentes níveis de consistência quando cria uma coleção, bem como quando efectua pesquisas e consultas.
Definir o nível de consistência ao criar uma coleção
Ao criar uma coleção, pode definir o nível de consistência para as pesquisas e consultas dentro da coleção. O exemplo de código a seguir define o nível de consistência como Bounded.
client.create_collection(
collection_name="my_collection",
schema=schema,
consistency_level="Bounded",
)
CreateCollectionReq createCollectionReq = CreateCollectionReq.builder()
.collectionName("my_collection")
.collectionSchema(schema)
.consistencyLevel(ConsistencyLevel.BOUNDED)
.build();
client.createCollection(createCollectionReq);
err = client.CreateCollection(ctx,
milvusclient.NewCreateCollectionOption("my_collection", schema).
WithConsistencyLevel(entity.ClBounded))
if err != nil {
fmt.Println(err.Error())
// handle error
}
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",
"isClusteringKey": true,
"elementTypeParams": {
"max_length": 512
}
}
]
}'
export params='{
"consistencyLevel": "Bounded"
}'
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
}"
Os valores possíveis para o parâmetro consistency_level são Strong, Bounded, Eventually e Session.
Definir o nível de consistência na pesquisa
É sempre possível alterar o nível de consistência de uma pesquisa específica. O exemplo de código a seguir define o nível de consistência de volta para Limitado. A alteração se aplica apenas à solicitação de pesquisa atual.
res = client.search(
collection_name="my_collection",
data=[query_vector],
limit=3,
search_params={"metric_type": "IP"},
consistency_level="Bounded",
)
SearchReq searchReq = SearchReq.builder()
.collectionName("my_collection")
.data(Collections.singletonList(queryVector))
.topK(3)
.searchParams(params)
.consistencyLevel(ConsistencyLevel.BOUNDED)
.build();
SearchResp searchResp = client.search(searchReq);
resultSets, err := client.Search(ctx, milvusclient.NewSearchOption(
"my_collection", // collectionName
3, // limit
[]entity.Vector{entity.FloatVector(queryVector)},
).WithConsistencyLevel(entity.ClBounded).
WithANNSField("vector"))
if err != nil {
fmt.Println(err.Error())
// handle error
}
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
"collectionName": "my_collection",
"data": [
[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]
],
"limit": 3,
"consistencyLevel": "Bounded"
}'
Este parâmetro também está disponível em pesquisas híbridas e no iterador de pesquisa. Os valores possíveis para o parâmetro consistency_level são Strong, Bounded, Eventually, e Session.
Definir o nível de consistência na consulta
É sempre possível alterar o nível de consistência para uma pesquisa específica. O exemplo de código a seguir define o nível de consistência para Eventually. A definição aplica-se apenas ao pedido de consulta atual.
res = client.query(
collection_name="my_collection",
filter="color like \"red%\"",
output_fields=["vector", "color"],
limit=3,
consistency_level="Eventually",
)
QueryReq queryReq = QueryReq.builder()
.collectionName("my_collection")
.filter("color like \"red%\"")
.outputFields(Arrays.asList("vector", "color"))
.limit(3)
.consistencyLevel(ConsistencyLevel.EVENTUALLY)
.build();
QueryResp getResp = client.query(queryReq);
resultSet, err := client.Query(ctx, milvusclient.NewQueryOption("my_collection").
WithFilter("color like \"red%\"").
WithOutputFields("vector", "color").
WithLimit(3).
WithConsistencyLevel(entity.ClEventually))
if err != nil {
fmt.Println(err.Error())
// handle error
}
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/query" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
"collectionName": "my_collection",
"filter": "color like \"red_%\"",
"consistencyLevel": "Bounded",
"limit": 3
}'
Este parâmetro também está disponível no iterador de consulta. Os valores possíveis para o parâmetro consistency_level são Strong, Bounded, Eventually, e Session.