Milvus 2.0 - Un aperçu des nouvelles fonctionnalités
Une demi-année s'est écoulée depuis la première version candidate de Milvus 2.0. Aujourd'hui, nous sommes fiers d'annoncer la disponibilité générale de Milvus 2.0. Suivez-moi pas à pas pour avoir un aperçu de certaines des nouvelles fonctionnalités prises en charge par Milvus.
Suppression d'entités
Milvus 2.0 prend en charge la suppression d'entités, ce qui permet aux utilisateurs de supprimer des vecteurs sur la base des clés primaires (ID) des vecteurs. Ils n'auront plus à s'inquiéter des données expirées ou non valides. Essayons-le.
- Connectez-vous à Milvus, créez une nouvelle collection et insérez 300 lignes de vecteurs à 128 dimensions générés de manière aléatoire.
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]
- Avant de procéder à la suppression, vérifiez que les entités que vous souhaitez supprimer existent par recherche ou par requête, et faites-le deux fois pour vous assurer que le résultat est 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}]
- Supprimez l'entité dont le cus_id est 76, puis recherchez et interrogez cette entité.
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
Pourquoi l'entité supprimée peut-elle encore être retrouvée ? Si vous avez vérifié le code source de Milvus, vous constaterez que la suppression dans Milvus est asynchrone et logique, ce qui signifie que les entités ne sont pas physiquement supprimées. Au lieu de cela, elles seront accompagnées d'une marque "supprimée" de sorte qu'aucune demande de recherche ou d'interrogation ne les récupérera. En outre, Milvus effectue par défaut des recherches sous le niveau de cohérence Bounded Staleness. Par conséquent, les entités supprimées peuvent encore être récupérées avant que les données ne soient synchronisées dans le nœud de données et le nœud d'interrogation. Essayez de rechercher ou d'interroger l'entité supprimée après quelques secondes, vous constaterez alors qu'elle ne figure plus dans les résultats.
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
Niveau de cohérence
L'expérience ci-dessus montre comment le niveau de cohérence influence la visibilité immédiate des données nouvellement supprimées. Les utilisateurs peuvent ajuster le niveau de cohérence de Milvus de manière flexible pour l'adapter à différents scénarios de service. Milvus 2.0 prend en charge quatre niveaux de cohérence :
CONSISTENCY_STRONG
Le niveau de cohérence de Milvus 2.0 prend en charge quatre niveaux de cohérence :GuaranteeTs
est défini comme identique à l'horodatage système le plus récent, et les nœuds de requête attendent que le temps de service passe à l'horodatage système le plus récent, puis traitent la requête de recherche ou d'interrogation.CONSISTENCY_EVENTUALLY
GuaranteeTs
Les nœuds d'interrogation attendent que le temps de service atteigne l'horodatage le plus récent du système, puis traitent la demande de recherche ou d'interrogation. Les nœuds de requête effectuent une recherche immédiate dans la vue des données existantes.CONSISTENCY_BOUNDED
GuaranteeTs
est défini comme étant relativement plus petit que l'horodatage le plus récent du système, et les nœuds d'interrogation effectuent une recherche sur une vue de données tolérable, moins mise à jour.CONSISTENCY_SESSION
: Le client utilise l'horodatage de la dernière opération d'écriture commeGuaranteeTs
, afin que chaque client puisse au moins récupérer les données qu'il a lui-même insérées.
Dans la version RC précédente, Milvus adopte Strong comme cohérence par défaut. Toutefois, compte tenu du fait que la plupart des utilisateurs sont moins exigeants en matière de cohérence que de performances, Milvus modifie la cohérence par défaut en Bounded Staleness, ce qui permet d'équilibrer leurs exigences dans une plus large mesure. À l'avenir, nous optimiserons davantage la configuration des GuaranteeT, qui ne peut être réalisée que lors de la création d'une collection dans la version actuelle. Pour plus d'informations sur GuaranteeTs
, voir l'horodatage de la garantie dans les requêtes de recherche.
Une cohérence moindre se traduit-elle par de meilleures performances ? Vous ne trouverez jamais la réponse tant que vous n'aurez pas essayé.
- Modifiez le code ci-dessus pour enregistrer la latence de la recherche.
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}")
- Effectuez une recherche avec une échelle de données et des paramètres identiques, sauf que
consistency_level
est défini commeCONSISTENCY_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
- Recherche dans une collection avec
consistency_level
défini commeCONSISTENCY_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
- Il est clair que le temps de latence moyen de la recherche dans la collection
CONSISTENCY_BOUNDED
est inférieur de 200 ms à celui de la collectionCONSISTENCY_STRONG
.
Les entités supprimées sont-elles immédiatement invisibles si le niveau de cohérence est défini comme Fort ? La réponse est oui. Vous pouvez toujours essayer par vous-même.
Transfert
Lorsqu'ils travaillent avec des ensembles de données en continu, de nombreux utilisateurs ont l'habitude de construire un index et de charger la collection avant d'y insérer des données. Dans les versions précédentes de Milvus, les utilisateurs doivent charger manuellement la collection après la construction de l'index pour remplacer les données brutes par l'index, ce qui est lent et laborieux. La fonction de transfert permet à Milvus 2.0 de charger automatiquement un segment indexé pour remplacer les données en continu qui atteignent certains seuils d'indexation, ce qui améliore considérablement les performances de recherche.
- Construire l'index et charger la collection avant d'insérer davantage d'entités.
# 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()
- Insérer 200 fois 50 000 lignes d'entités (les mêmes lots de vecteurs sont utilisés pour des raisons de commodité, mais cela n'affectera pas le résultat).
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}")
- Vérifiez les informations relatives au chargement des segments dans le nœud de requête pendant et après l'insertion.
# did this in another python console
utility.get_query_segment_info("hello_milmil_handoff")
- Vous constaterez que tous les segments scellés chargés dans le nœud de requête sont indexés.
[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
...
De plus, les fonctionnalités suivantes sont disponibles
Outre les fonctionnalités ci-dessus, de nouvelles fonctionnalités telles que le compactage des données, l'équilibrage dynamique des charges, etc. sont introduites dans Milvus 2.0. Nous vous souhaitons un bon voyage exploratoire avec Milvus !
Dans un avenir proche, nous partagerons avec vous une série de blogs présentant la conception des nouvelles fonctionnalités de Milvus 2.0.
Retrouvez-nous sur :
- Suppression d'entités
- Niveau de cohérence
- Transfert
- De plus, les fonctionnalités suivantes sont disponibles
On This Page
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word