🚀 Essayez Zilliz Cloud, la version entièrement gérée de Milvus, gratuitement—découvrez des performances 10x plus rapides ! Essayez maintenant>>

milvus-logo
LFAI

Conclusion

  • Engineering
August 22, 2020
milvus

Cet article traite de la manière dont Milvus met en œuvre la fonction de suppression. Fonction très attendue par de nombreux utilisateurs, la fonction de suppression a été introduite dans Milvus v0.7.0. Nous n'avons pas appelé directement remove_ids dans FAISS, mais nous avons mis au point une toute nouvelle conception pour rendre la suppression plus efficace et prendre en charge davantage de types d'index.

Dans Comment Milvus réalise la mise à jour dynamique des données et les requêtes, nous avons présenté l'ensemble du processus, de l'insertion des données à la suppression des données. Récapitulons certaines des bases. MemManager gère tous les tampons d'insertion, chaque MemTable correspondant à une collection (nous avons renommé "table" en "collection" dans Milvus v0.7.0). Milvus divise automatiquement les données insérées dans la mémoire en plusieurs MemTableFiles. Lorsque les données sont transférées sur le disque, chaque MemTableFile est sérialisé en un fichier brut. Nous avons conservé cette architecture lors de la conception de la fonction de suppression.

Nous définissons la fonction de la méthode delete comme la suppression de toutes les données correspondant aux ID d'entité spécifiés dans une collection spécifique. Lors du développement de cette fonction, nous avons conçu deux scénarios. Le premier consiste à supprimer les données qui se trouvent encore dans la mémoire tampon d'insertion et le second à supprimer les données qui ont été transférées sur le disque. Le premier scénario est plus intuitif. Nous pouvons trouver le fichier MemTableFile correspondant à l'ID spécifié et supprimer directement les données en mémoire (figure 1). Étant donné que la suppression et l'insertion de données ne peuvent être effectuées en même temps, et en raison du mécanisme qui fait passer le fichier MemTableFile de mutable à immuable lors de la vidange des données, la suppression n'est effectuée que dans la mémoire tampon mutable. De cette manière, l'opération de suppression n'entre pas en conflit avec l'extraction de données, ce qui garantit la cohérence des données.

1-delete-request-milvus.jpg 1-delete-request-milvus.jpg

Le deuxième scénario est plus complexe mais plus courant, car dans la plupart des cas, les données restent brièvement dans le tampon d'insertion avant d'être transférées sur le disque. Étant donné qu'il est très inefficace de charger les données transférées dans la mémoire pour les effacer, nous avons décidé d'opter pour un effacement en douceur, une approche plus efficace. Au lieu de supprimer réellement les données transférées, la suppression douce enregistre les identifiants supprimés dans un fichier distinct. De cette manière, nous pouvons filtrer ces identifiants supprimés lors des opérations de lecture, telles que la recherche.

En ce qui concerne la mise en œuvre, nous devons tenir compte de plusieurs points. Dans Milvus, les données ne sont visibles ou, en d'autres termes, récupérables, que lorsqu'elles sont transférées sur le disque. Par conséquent, les données transférées ne sont pas supprimées lors de l'appel à la méthode de suppression, mais lors de l'opération de transfert suivante. En effet, les fichiers de données qui ont été transférés sur le disque ne contiennent plus de nouvelles données, de sorte que l'effacement progressif n'a pas d'incidence sur les données qui ont été transférées. Lors de l'appel à la fonction de suppression, vous pouvez directement supprimer les données qui se trouvent encore dans le tampon d'insertion, alors que pour les données transférées, vous devez enregistrer l'ID des données supprimées dans la mémoire. Lors du transfert des données sur le disque, Milvus écrit l'ID supprimé dans le fichier DEL pour enregistrer l'entité du segment correspondant qui est supprimée. Ces mises à jour ne seront visibles qu'une fois le transfert de données terminé. Ce processus est illustré à la figure 2. Avant la version 0.7.0, nous ne disposions que d'un mécanisme de vidange automatique, c'est-à-dire que Milvus sérialise les données dans le tampon d'insertion toutes les secondes. Dans notre nouvelle conception, nous avons ajouté une méthode de rinçage permettant aux développeurs d'appeler après la méthode de suppression, ce qui garantit que les données nouvellement insérées sont visibles et que les données supprimées ne sont plus récupérables.

2-delete-request-milvus.jpg 2-delete-request-milvus.jpg

Le deuxième problème est que le fichier de données brutes et le fichier d'index sont deux fichiers distincts dans Milvus, et deux enregistrements indépendants dans les métadonnées. Lors de la suppression d'un identifiant spécifié, nous devons trouver le fichier brut et le fichier d'index correspondant à l'identifiant et les enregistrer ensemble. C'est pourquoi nous avons introduit le concept de segment. Un segment contient le fichier brut (qui comprend les fichiers vectoriels bruts et les fichiers d'identification), le fichier d'index et le fichier DEL. Le segment est l'unité de base pour la lecture, l'écriture et la recherche de vecteurs dans Milvus. Une collection (figure 3) est composée de plusieurs segments. Il existe donc plusieurs dossiers de segments sous un dossier de collection sur le disque. Comme nos métadonnées sont basées sur des bases de données relationnelles (SQLite ou MySQL), il est très simple d'enregistrer la relation au sein d'un segment, et l'opération de suppression ne nécessite plus le traitement séparé du fichier brut et du fichier d'index.

3-delete-request-milvus.jpg 3-delete-request-milvus.jpg

La troisième question est de savoir comment filtrer les données supprimées au cours d'une recherche. En pratique, l'ID enregistré par DEL est le décalage des données correspondantes stockées dans le segment. Étant donné que le segment effacé n'inclut pas de nouvelles données, l'offset ne changera pas. La structure de données de DEL est un tableau de bits dans la mémoire, où un bit actif représente un décalage supprimé. Nous avons également mis à jour FAISS en conséquence : lorsque vous effectuez une recherche dans FAISS, le vecteur correspondant au bit actif ne sera plus inclus dans le calcul de la distance (figure 4). Les modifications apportées à FAISS ne seront pas abordées en détail ici.

4-delete-request-milvus.jpg 4-delete-request-milvus.jpg

Le dernier point concerne l'amélioration des performances. Lors de la suppression de données effacées, il faut d'abord déterminer dans quel segment de la collection se trouve l'identifiant supprimé, puis enregistrer son décalage. L'approche la plus simple consiste à rechercher tous les identifiants dans chaque segment. L'optimisation à laquelle nous pensons consiste à ajouter un filtre de Bloom à chaque segment. Le filtre de Bloom est une structure de données aléatoires utilisée pour vérifier si un élément est membre d'un ensemble. Par conséquent, nous pouvons charger uniquement le filtre de Bloom de chaque segment. Ce n'est que lorsque le filtre de Bloom détermine que l'ID supprimé se trouve dans le segment actuel que nous pouvons trouver le décalage correspondant dans le segment ; sinon, nous pouvons ignorer ce segment (figure 5). Nous avons choisi le filtre de Bloom parce qu'il utilise moins d'espace et qu'il est plus efficace dans la recherche que beaucoup de ses homologues, tels que les tables de hachage. Bien que le filtre bloom ait un certain taux de faux positifs, nous pouvons réduire les segments à rechercher au nombre idéal pour ajuster la probabilité. Parallèlement, le filtre de Bloom doit également prendre en charge la suppression. Dans le cas contraire, l'identifiant de l'entité supprimée peut toujours être trouvé dans le filtre de Bloom, ce qui augmente le taux de faux positifs. C'est la raison pour laquelle nous utilisons le filtre de floraison de comptage, qui prend en charge la suppression. Dans cet article, nous ne détaillerons pas le fonctionnement du filtre de Bloom. Vous pouvez vous référer à Wikipedia si cela vous intéresse.

5-delete-request-milvus.jpg 5-delete-request-milvus.jpg

Conclusion

Jusqu'à présent, nous vous avons présenté brièvement la manière dont Milvus supprime les vecteurs par ID. Comme vous le savez, nous utilisons la suppression douce pour supprimer les données effacées. Au fur et à mesure que le nombre de données supprimées augmente, nous devons compacter les segments de la collection pour libérer l'espace occupé par les données supprimées. En outre, si un segment a déjà été indexé, le compactage supprime également le fichier d'index précédent et crée de nouveaux index. Pour l'instant, les développeurs doivent appeler la méthode compact pour compacter les données. À l'avenir, nous espérons introduire un mécanisme d'inspection. Par exemple, lorsque la quantité de données supprimées atteint un certain seuil ou que la distribution des données a changé après une suppression, Milvus compacte automatiquement le segment.

Nous avons maintenant présenté la philosophie de conception de la fonction de suppression et sa mise en œuvre. Il y a certainement matière à amélioration, et tous vos commentaires ou suggestions sont les bienvenus.

Pour en savoir plus sur Milvus : https://github.com/milvus-io/milvus. Vous pouvez également rejoindre notre communauté Slack pour des discussions techniques !

    Try Managed Milvus for Free

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

    Get Started

    Like the article? Spread the word

    Continuer à Lire