Nivel de consistencia
Como base de datos vectorial distribuida, Milvus ofrece múltiples niveles de consistencia para asegurar que cada nodo o réplica pueda acceder a los mismos datos durante las operaciones de lectura y escritura. Actualmente, los niveles de consistencia soportados incluyen Strong, Bounded, Eventually y Session, siendo Bounded el nivel de consistencia utilizado por defecto.
Visión general
Milvus es un sistema que separa el almacenamiento y la computación. En este sistema, los DataNodes son responsables de la persistencia de los datos y, en última instancia, los almacenan en un almacenamiento de objetos distribuido como MinIO/S3. Los QueryNodes se encargan de tareas computacionales como la búsqueda. Estas tareas implican el procesamiento tanto de datos por lotes como de datos en flujo. En pocas palabras, los datos por lotes pueden entenderse como datos que ya se han almacenado en el almacenamiento de objetos, mientras que los datos de flujo se refieren a datos que aún no se han almacenado en el almacenamiento de objetos. Debido a la latencia de la red, los QueryNodes a menudo no contienen los datos de flujo más recientes. Sin salvaguardas adicionales, realizar una búsqueda directamente en los datos de flujo puede resultar en la pérdida de muchos puntos de datos no comprometidos, afectando a la precisión de los resultados de la búsqueda.
Milvus Commercial Edition es un sistema que separa el almacenamiento y el cálculo. En este sistema, los DataNodes son responsables de la persistencia de los datos y, en última instancia, los almacenan en un almacenamiento de objetos distribuido como MinIO/S3. Los QueryNodes se encargan de tareas computacionales como la búsqueda. Estas tareas implican el procesamiento tanto de datos por lotes como de datos en flujo. En pocas palabras, los datos por lotes pueden entenderse como datos que ya se han almacenado en el almacenamiento de objetos, mientras que los datos de flujo se refieren a datos que aún no se han almacenado en el almacenamiento de objetos. Debido a la latencia de la red, los QueryNodes a menudo no contienen los datos de flujo más recientes. Sin salvaguardas adicionales, realizar una búsqueda directamente en los datos de flujo puede provocar la pérdida de muchos puntos de datos no comprometidos, lo que afectaría a la precisión de los resultados de la búsqueda.
Datos por lotes y datos en flujo
Como se muestra en la figura anterior, los QueryNodes pueden recibir simultáneamente datos de flujo y datos por lotes tras recibir una solicitud de búsqueda. Sin embargo, debido a la latencia de la red, los datos de flujo obtenidos por los QueryNodes pueden estar incompletos.
Para solucionar este problema, Milvus marca el tiempo de cada registro en la cola de datos e inserta continuamente marcas de tiempo de sincronización en la cola de datos. Cada vez que se recibe una marca de tiempo de sincronización (syncTs), QueryNodes la establece como la Hora de Servicio, lo que significa que QueryNodes puede ver todos los datos anteriores a esa Hora de Servicio. Basándose en el ServiceTime, Milvus puede proporcionar marcas de tiempo de garantía (GuaranteeTs) para satisfacer los diferentes requisitos de los usuarios en cuanto a consistencia y disponibilidad. Los usuarios pueden informar a los Nodos de Consulta de la necesidad de incluir datos anteriores a un momento determinado en el ámbito de la búsqueda especificando los GuaranteeTs en sus peticiones de Búsqueda.
ServiceTime y GuaranteeTs
Como se muestra en la figura anterior, si GuaranteeTs es menor que ServiceTime, significa que todos los datos anteriores al momento especificado se han escrito completamente en el disco, lo que permite a los QueryNodes realizar inmediatamente la operación de búsqueda. Cuando GuaranteeTs es mayor que ServiceTime, los QueryNodes deben esperar hasta que ServiceTime supere a GuaranteeTs antes de poder ejecutar la operación de búsqueda.
Los usuarios deben buscar un equilibrio entre la precisión y la latencia de la consulta. Si los usuarios tienen altos requisitos de consistencia y no son sensibles a la latencia de la consulta, pueden establecer GuaranteeTs a un valor tan grande como sea posible; si los usuarios desean recibir resultados de búsqueda rápidamente y son más tolerantes a la precisión de la consulta, entonces GuaranteeTs se puede establecer a un valor más pequeño.
Ilustración de los niveles de coherencia
Milvus proporciona cuatro tipos de niveles de consistencia con diferentes GuaranteeTs.
Fuerte
La última marca de tiempo se utiliza como GuaranteeTs, y los QueryNodes tienen que esperar hasta que el ServiceTime cumpla con los GuaranteeTs antes de ejecutar las peticiones de Búsqueda.
Eventual
El GuaranteeTs se establece en un valor extremadamente pequeño, como 1, para evitar comprobaciones de consistencia y que los QueryNodes puedan ejecutar inmediatamente peticiones de búsqueda sobre todos los datos del lote.
Estancamiento limitado
El GuranteeTs se establece en un punto de tiempo anterior a la última marca de tiempo para hacer que los QueryNodes realicen búsquedas con una tolerancia de cierta pérdida de datos.
Sesión
El último punto temporal en el que el cliente inserta datos se utiliza como el GuaranteeTs para que los QueryNodes puedan realizar búsquedas sobre todos los datos insertados por el cliente.
Milvus utiliza Bounded Staleness como nivel de consistencia por defecto. Si no se especifica el GuaranteeTs, se utiliza el último ServiceTime como GuaranteeTs.
Establecer nivel de consistencia
Puede establecer diferentes niveles de consistencia al crear una colección, así como al realizar búsquedas y consultas.
Establecer nivel de coherencia al crear una colección
Al crear una colección, puede establecer el nivel de coherencia para las búsquedas y consultas dentro de la colección. El siguiente ejemplo de código establece el nivel de consistencia en Fuerte.
client.create_collection(
collection_name="my_collection",
schema=schema,
# highlight-next
consistency_level="Strong",
)
CreateCollectionReq createCollectionReq = CreateCollectionReq.builder()
.collectionName("my_collection")
.collectionSchema(schema)
// highlight-next
.consistencyLevel(ConsistencyLevel.STRONG)
.build();
client.createCollection(createCollectionReq);
export schema='{
"autoId": true,
"enabledDynamicField": false,
"fields": [
{
"fieldName": "my_id",
"dataType": "Int64",
"isPrimary": true
},
{
"fieldName": "my_vector",
"dataType": "FloatVector",
"elementTypeParams": {
"dim": "5"
}
},
{
"fieldName": "my_varchar",
"dataType": "VarChar",
"isClusteringKey": true,
"elementTypeParams": {
"max_length": 512
}
}
]
}'
export params='{
"consistencyLevel": "Strong"
}'
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
}"
Los valores posibles para el parámetro consistency_level
son Strong
, Bounded
, Eventually
y Session
.
Establecer el nivel de consistencia en la búsqueda
Siempre puede cambiar el nivel de consistencia para una búsqueda específica. El siguiente ejemplo de código vuelve a establecer el nivel de consistencia en Limitado. El cambio sólo se aplica a la solicitud de búsqueda actual.
res = client.search(
collection_name="my_collection",
data=[query_vector],
limit=3,
search_params={"metric_type": "IP"},
# highlight-start
consistency_level="Bounded",
# highlight-next
)
SearchReq searchReq = SearchReq.builder()
.collectionName("my_collection")
.data(Collections.singletonList(queryVector))
.topK(3)
.searchParams(params)
.consistencyLevel(ConsistencyLevel.BOUNDED)
.build();
SearchResp searchResp = client.search(searchReq);
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 también está disponible en las búsquedas híbridas y en el iterador de búsqueda. Los valores posibles para el parámetro consistency_level
son Strong
, Bounded
, Eventually
y Session
.
Establecer el nivel de consistencia en la consulta
Siempre puede cambiar el nivel de consistencia para una búsqueda específica. El siguiente ejemplo de código establece el nivel de consistencia en Eventualmente. El ajuste sólo se aplica a la solicitud de consulta actual.
res = client.query(
collection_name="my_collection",
filter="color like \"red%\"",
output_fields=["vector", "color"],
limit=3,
# highlight-start
consistency_level="Eventually",
# highlight-next
)
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);
Este parámetro también está disponible en el iterador de consulta. Los valores posibles para el parámetro consistency_level
son Strong
, Bounded
, Eventually
y Session
.