🚀 Prueba Zilliz Cloud, el Milvus completamente gestionado, gratis—¡experimenta un rendimiento 10 veces más rápido! Prueba Ahora>>

milvus-logo
LFAI
  • Home
  • Blog
  • Milvus 2.0 - Un vistazo a las nuevas funciones

Milvus 2.0 - Un vistazo a las nuevas funciones

  • Engineering
January 27, 2022
Yanliang Qiao

Ha pasado medio año desde la primera versión candidata de Milvus 2.0. Ahora estamos orgullosos de anunciar la disponibilidad general de Milvus 2.0. Por favor, sígame paso a paso para echar un vistazo a algunas de las nuevas características que soporta Milvus.

Eliminación de entidades

Milvus 2.0 soporta el borrado de entidades, permitiendo a los usuarios borrar vectores basándose en las claves primarias (IDs) de los vectores. Ya no tendrán que preocuparse por los datos caducados o no válidos. Probémoslo.

  1. Conéctese a Milvus, cree una nueva colección e inserte 300 filas de vectores de 128 dimensiones generados aleatoriamente.
from pymilvus import connections, utility, Collection, DataType, FieldSchema, CollectionSchema
# connect to milvus
host = 'x.x.x.x'
connections.add_connection(default={"host": host, "port": 19530})
connections.connect(alias='default')
# create a collection with customized primary field: id_field
dim = 128
id_field = FieldSchema(name="cus_id", dtype=DataType.INT64, is_primary=True)
age_field = FieldSchema(name="age", dtype=DataType.INT64, description="age")
embedding_field = FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=dim)
schema = CollectionSchema(fields=[id_field, age_field, embedding_field],
                          auto_id=False, description="hello MilMil")
collection_name = "hello_milmil"
collection = Collection(name=collection_name, schema=schema)
import random
# insert data with customized ids
nb = 300
ids = [i for i in range(nb)]
ages = [random.randint(20, 40) for i in range(nb)]
embeddings = [[random.random() for _ in range(dim)] for _ in range(nb)]
entities = [ids, ages, embeddings]
ins_res = collection.insert(entities)
print(f"insert entities primary keys: {ins_res.primary_keys}")
insert entities primary keys: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299]
  1. Antes de proceder a la eliminación, compruebe que las entidades que desea eliminar existen mediante búsqueda o consulta, y hágalo dos veces para asegurarse de que el resultado es fiable.
# search
nq = 10
search_vec = [[random.random() for _ in range(dim)] for _ in range(nq)]
search_params = {"metric_type": "L2", "params": {"nprobe": 16}}
limit = 3
# search 2 times to verify the vector persists
for i in range(2):
    results = collection.search(search_vec, embedding_field.name, search_params, limit)
    ids = results[0].ids
    print(f"search result ids: {ids}")
    expr = f"cus_id in {ids}"
    # query to verify the ids exist
    query_res = collection.query(expr)
    print(f"query results: {query_res}")
search result ids: [76, 2, 246]
query results: [{'cus_id': 246}, {'cus_id': 2}, {'cus_id': 76}]
search result ids: [76, 2, 246]
query results: [{'cus_id': 246}, {'cus_id': 2}, {'cus_id': 76}]
  1. Borre la entidad con el cus_id de 76, y luego busque y consulte por esta entidad.
print(f"trying to delete one vector: id={ids[0]}")
collection.delete(expr=f"cus_id in {[ids[0]]}")
results = collection.search(search_vec, embedding_field.name, search_params, limit)
ids = results[0].ids
print(f"after deleted: search result ids: {ids}")
expr = f"cus_id in {ids}"
# query to verify the id exists
query_res = collection.query(expr)
print(f"after deleted: query res: {query_res}")
print("completed")
trying to delete one vector: id=76
after deleted: search result ids: [76, 2, 246]
after deleted: query res: [{'cus_id': 246}, {'cus_id': 2}, {'cus_id': 76}]
completed

¿Por qué se puede recuperar la entidad eliminada? Si ha revisado el código fuente de Milvus, encontrará que la eliminación dentro de Milvus es asíncrona y lógica, lo que significa que las entidades no serán eliminadas físicamente. En su lugar, se adjuntarán con una marca de "borrado" para que ninguna petición de búsqueda o consulta las recupere. Además, Milvus busca bajo el nivel de consistencia Bounded Staleness por defecto. Por lo tanto, las entidades eliminadas aún son recuperables antes de que los datos se sincronicen en el nodo de datos y el nodo de consulta. Intente buscar o consultar la entidad eliminada después de unos segundos, entonces encontrará que ya no está en el resultado.

expr = f"cus_id in {[76, 2, 246]}"
# query to verify the id exists
query_res = collection.query(expr)
print(f"after deleted: query res: {query_res}")
print("completed")
after deleted: query res: [{'cus_id': 246}, {'cus_id': 2}]
completed

Nivel de coherencia

El experimento anterior nos muestra cómo el nivel de consistencia influye en la visibilidad inmediata de los datos recién borrados. Los usuarios pueden ajustar el nivel de consistencia de Milvus de forma flexible para adaptarlo a diversos escenarios de servicio. Milvus 2.0 admite cuatro niveles de consistencia:

  • CONSISTENCY_STRONG: GuaranteeTs se establece como idéntico a la marca de tiempo más reciente del sistema, y los nodos de consulta esperan hasta que el tiempo de servicio proceda a la marca de tiempo más reciente del sistema, y luego procesan la solicitud de búsqueda o consulta.
  • CONSISTENCY_EVENTUALLYPara omitir la comprobación de coherencia: GuaranteeTs se establece como insignificantemente menor que la marca de tiempo más reciente del sistema para omitir la comprobación de coherencia. Los nodos de consulta buscan inmediatamente en la vista de datos existente.
  • CONSISTENCY_BOUNDED GuaranteeTs se establece relativamente más pequeño que la marca de tiempo más reciente del sistema, y los nodos de consulta buscan en una vista de datos tolerable, menos actualizada.
  • CONSISTENCY_SESSION: El cliente utiliza la marca de tiempo de la última operación de escritura como GuaranteeTs, de modo que cada cliente pueda al menos recuperar los datos insertados por sí mismo.

En la versión RC anterior, Milvus adopta Strong como consistencia por defecto. Sin embargo, teniendo en cuenta el hecho de que la mayoría de los usuarios son menos exigentes con la consistencia que con el rendimiento, Milvus cambia la consistencia por defecto a Bounded Staleness, que puede equilibrar sus requisitos en mayor medida. En el futuro, optimizaremos aún más la configuración de las GuaranteeTs, lo que en la versión actual sólo puede conseguirse durante la creación de la colección. Para obtener más información sobre GuaranteeTs, consulte Fechas y horas de garantía en las solicitudes de búsqueda.

¿Una menor coherencia dará lugar a un mejor rendimiento? La respuesta no se encuentra hasta que no se prueba.

  1. Modifique el código anterior para registrar la latencia de la búsqueda.
for i in range(5):
    start = time.time()
    results = collection.search(search_vec, embedding_field.name, search_params, limit)
    end = time.time()
    print(f"search latency: {round(end-start, 4)}")
    ids = results[0].ids
    print(f"search result ids: {ids}")
  1. Busque con la escala de datos y los parámetros idénticos, excepto que consistency_level se establece como CONSISTENCY_STRONG.
collection_name = "hello_milmil_consist_strong"
collection = Collection(name=collection_name, schema=schema,
                        consistency_level=CONSISTENCY_STRONG)
search latency: 0.3293
search latency: 0.1949
search latency: 0.1998
search latency: 0.2016
search latency: 0.198
completed
  1. Búsqueda en una colección con consistency_level establecido como CONSISTENCY_BOUNDED.
collection_name = "hello_milmil_consist_bounded"
collection = Collection(name=collection_name, schema=schema,
                        consistency_level=CONSISTENCY_BOUNDED)
search latency: 0.0144
search latency: 0.0104
search latency: 0.0107
search latency: 0.0104
search latency: 0.0102
completed
  1. Claramente, la latencia media de búsqueda en la colección CONSISTENCY_BOUNDED es 200ms menor que en la colección CONSISTENCY_STRONG.

¿Son las entidades eliminadas inmediatamente invisibles si el nivel de consistencia se establece como Fuerte? La respuesta es sí. Puede probarlo por su cuenta.

Traspaso

Al trabajar con conjuntos de datos en streaming, muchos usuarios están acostumbrados a crear un índice y cargar la colección antes de insertar datos en ella. En versiones anteriores de Milvus, los usuarios tienen que cargar la colección manualmente después de la construcción del índice para reemplazar los datos brutos con el índice, lo que es lento y laborioso. La función handoff permite a Milvus 2.0 cargar automáticamente el segmento indexado para sustituir los datos en flujo que alcanzan determinados umbrales de indexación, lo que mejora enormemente el rendimiento de la búsqueda.

  1. Construya el índice y cargue la colección antes de insertar más entidades.
# index
index_params = {"index_type": "IVF_SQ8", "metric_type": "L2", "params": {"nlist": 64}}
collection.create_index(field_name=embedding_field.name, index_params=index_params)
# load
collection.load()
  1. Inserte 50.000 filas de entidades 200 veces (se utilizan los mismos lotes de vectores por comodidad, pero esto no afectará al resultado).
import random
# insert data with customized ids
nb = 50000
ids = [i for i in range(nb)]
ages = [random.randint(20, 40) for i in range(nb)]
embeddings = [[random.random() for _ in range(dim)] for _ in range(nb)]
entities = [ids, ages, embeddings]
for i in range(200):
    ins_res = collection.insert(entities)
    print(f"insert entities primary keys: {ins_res.primary_keys}")
  1. Compruebe la información del segmento de carga en el nodo de consulta durante y después de la inserción.
# did this in another python console
utility.get_query_segment_info("hello_milmil_handoff")
  1. Comprobará que todos los segmentos sellados cargados en el nodo de consulta están indexados.
[segmentID: 430640405514551298
collectionID: 430640403705757697
partitionID: 430640403705757698
mem_size: 394463520
num_rows: 747090
index_name: "_default_idx"
indexID: 430640403745079297
nodeID: 7
state: Sealed
, segmentID: 430640405514551297
collectionID: 430640403705757697
partitionID: 430640403705757698
mem_size: 397536480
num_rows: 752910
index_name: "_default_idx"
indexID: 430640403745079297
nodeID: 7
state: Sealed
...

Además

Además de las funcionalidades anteriores, en Milvus 2.0 se han introducido nuevas características como la compactación de datos, el equilibrio de carga dinámico, etc. Disfrute de su viaje exploratorio con Milvus.

En un futuro próximo, compartiremos con usted una serie de blogs que presentarán el diseño de las nuevas funciones de Milvus 2.0.

Encuéntrenos en:

Try Managed Milvus for Free

Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.

Get Started

Like the article? Spread the word

Sigue Leyendo