Base de données vectorielle

Base de données vectorielle : l’architecture de Milvus décortiquée

Analyse technique approfondie COBOLAvancé

Base de données vectorielle : l'architecture de Milvus décortiquée

L’indexation de vecteurs haute dimension pose un défi de latence critique pour les systèmes distribués. Une base de données vectorielle comme Milvus doit garantir un rappel élevé sans sacrifier le débit.

Le passage de recherches exactes à des recherches approximatives (ANN) modifie radicalement la gestion de la mémoire. Milvus 2.4 s’appuie sur une architecture découpée pour gérer des milliards de vecteurs.

Vous apprendrez à analyser la structure des index HNSW et les mécanismes de partitionnement de Milvus.

Base de données vectorielle

🛠️ Prérequis

Installation des dépendances pour tester l’intégration via un bridge Python/C.

  • Docker 24.0+ pour le déploiement de Milvus 2.4.x
  • Python 3.12 pour le client PyMilvus
  • GnuCOBOL 3.2 pour la simulation de structures de données legacy
  • L’outil ‘pip’ pour installer pymilvus

📚 Comprendre Base de données vectorielle

Une base de données vectorielle ne stocke pas des relations tabulaires simples. Elle stocke des embeddings, des représentations numériques de données non structurées.

L’algorithme HNSW (Hierarchical Navigable Small World) est le cœur du moteur. Il construit un graphe multi-couches. Les couches supérieures contiennent peu de points pour une navigation rapide. Les couches inférieures augmentent la densité pour la précision.

Comparaison avec un index B-Tree classique :

  • B-Tree : Complexité O(log N) pour des clés scalaires.
  • HNSW : Complexité approximative pour des distances euclidiennes.
  • Structure : Le B-Tree est hiérarchique et statique. Le graphe HNSW est dynamique et probabiliste.

Le mécanisme de partitionnement de Milvus repose sur des segments. Chaque segment est une unité d’écriture et d’indexation indépendante. Cela permet une scalabilité horizontale sans verrouillage global.

🏦 Le code — Base de données vectorielle

COBOL
IDENTIFICATION DIVISION.
PROGRAM-ID. VEC-STRUCT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 VECTOR-RECORD.
   05 VECTOR-ID          PIC 9(08).
   05 VECTOR-DIMENSION   PIC 9(03).
   05 VECTOR-VALUES      OCCURS 128 TIMES DEPENDING ON VECTOR-DIMENSION.
      10 V-VAL           PIC S9(8)V9(8) COMP-3.
01 SEARCH-RESULT.
   05 RESULT-ID          PIC 9(08).
   05 DISTANCE-SCORE     PIC S9(8)V9(8) COMP-3.
PROCEDURE DIVISION.
    MOVE 1001 TO VECTOR-ID.
    MOVE 128 TO VECTOR-DIMENSION.
    * Simulation d'initialisation de vecteur
    PERFORM VARYING I FROM 1 BY 1 UNTIL I > 128
        COMPUTE V-VAL(I) = FUNCTION RANDOM * 1.0
    END-PERFORM.
    DISPLAY "Vecteur initialise : " VECTOR-ID.
    STOP RUN.

📖 Explication

Le premier snippet COBOL définit une structure de données fixe. L’utilisation de ‘COMP-3’ (Packed Decimal) simule la densité des données numériques. On utilise ‘OCCURS 128 TIMES’ pour représenter un vecteur de dimension 128. C’est une structure typique des systèmes mainframe traitant des flux batch.

Le second snippet implémente l’algorithme de distance euclidienne. La boucle ‘PERFORM VARYING’ parcourt les dimensions. On calcule la somme des carrés des différences. C’est l’opération de base d’une base de données vectorielle lors d’une recherche de proximité.

Attention au piège du type de données. En COBOL, le calcul de racine carrée nécessite une utilisation précise de la clause ‘COMPUTE’. L’utilisation de types flottants non supportés nativement peut entraîner des erreurs d’arrondi massives.

Documentation officielle COBOL

🔄 Second exemple

COBOL
IDENTIFICATION DIVISION.
PROGRAM-ID. VEC-SEARCH-SIM.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 QUERY-VECTOR OCCURS 128 TIMES PIC S9(8)V9(8) COMP-3.
01 TARGET-VECTOR OCCURS 128 TIMES PIC S9(8)V9(8) COMP-3.
01 DISTANCE           PIC S9(8)V9(8) COMP-3.
01 I                  PIC 9(3).
PROCEDURE DIVISION.
    * Calcul de la distance Euclidienne simplifie
    MOVE 0 TO DISTANCE.
    PERFORM VARYY-LOOP.
    DISPLAY "Distance calculee : " DISTANCE.
    STOP RUN.
    
    PROCEDURE VARYY-LOOP.
    PERFORM VARYING I FROM 1 BY 1 UNTIL I > 128
        COMPUTE DISTANCE = DISTANCE + (QUERY-VECTOR(I) - TARGET-VECTOR(I)) * 
                           (QUERY-VECTOR(I) - TARGET-VECTOR(I))
    END-PERFORM.
    COMPUTE DISTANCE = SQRT(DISTANCE).

Analyse technique approfondie

L’architecture de Milvus est décomposée en quatre composants distincts : Proxy, Query Node, Data Node et Index Node. Cette séparation est cruciale pour la gestion de la charge. Le Proxy reçoit les requêtes gRPC. Il les distribue aux Query Nodes.

Le Data Node gère l’ingestion des flux. Il écrit les données dans le Log Broker, souvent basé sur Pulsar ou Kafka. Cette architecture garantit que l’écriture n’est pas bloquée par la reconstruction des index. Une base de données vectorielle doit maintenir une cohérence forte lors de l’écriture.

L’implémentation de l’index HNSW dans Milvus présente des contraintes de mémoire. Chaque lien dans le graphe occupe de l’espace. Pour un vecteur de dimension 768, l’overhead peut dépasser 50% de la taille brute des données. Dans la version 2.3, l’optimisation des structures de données a réduit cet impact.

<

Le mécanisme de ‘Compaction’ est un autre point critique. Milvus fusionne les segments de données plus petits en segments plus grands. Cela réduit la fragmentation. Cependant, cette opération consomme des ressources CPU importantes sur les Index Nodes. Si vous configurez trop de segments petits, la fréquence des compactions augmentera la latence de recherche.

Le paramètre ‘ef’ (search efficiency) contrôle le nombre de candidats examinés. Un ‘ef’ élevé augmente le rappel (recall) mais dégrade le temps de réponse. En pratique, un ‘ef’ de 64 offre un bon compromlet pour la plupart des usages. Pour des besoins critiques, on peut monter à 256, au prix d’une augmentation de la latence de 3x à 5x.

▶️ Exemple d’utilisation

Exécution d’un script de test de distance avec le simulateur COBOL.

# Compilation
cobc -x -o vec_test vec_test.cbl

# Exécution
./vec_test

# Sortie attendue
Vecteur initialise : 1001

🚀 Cas d’usage avancés

1. Recherche d’images par similarité : Utilisation de modèles ResNet pour générer des vecteurs. Stockage des embeddings dans Milvus. La requête compare l’embedding de l’image source avec la base.

2. Système de recommandation temps réel : Intégration de logs d’utilisateurs. Chaque action utilisateur est transformée en vecteur. Recherche des produits les plus proches dans l’espace vectoriel via l’API Milvus.

3. Détection d’anomalies dans les logs système : Transformation de séquences de logs en vecteurs. Une distance euclidienne élevée entre un log actuel et les patterns normaux déclenche une alerte.

✅ Bonnes pratiques

Pour gérer une base de données vectorielle en production, respectez ces règles :

  • Utilisez toujours le mode ‘Load’ avant toute recherche pour charger les segments en RAM.
  • Configurez le paramètre ‘M’ de l’index HNSW en fonction de la dimensionnalité.
  • Séparez les nœuds de calcul (Query Nodes) des nœuds d’écriture (Data Nodes).
  • Surveillez la métrique ‘Recall’ pour valiter la précision de l’index.
  • Implémentez une stratégie de partitionnement basée sur le temps ou la région.
Points clés

  • Milvus utilise une architecture découplée pour la scalabilité.
  • HNSW est l'algorithme de référence pour la recherche rapide.
  • Le coût mémoire est proportionnel à la dimensionnalité des vecteurs.
  • La gestion des segments est vitale pour la cohérence des données.
  • Le paramètre 'ef' influence directement le compromis latence/précision.
  • L'indexation nécessite une phase de chargement explicite en mémoire.
  • La détection d'anomalies est un cas d'usage majeur pour les embeddings.
  • L'utilisation de gRPC est le standard pour les échanges avec Milvus.

❓ Questions fréquentes

Peut-on utiliser Milvus pour des recherches exactes ?

Oui, en utilisant l’index de type ‘FLAT’. Cependant, la complexité devient O(N), ce qui est inefficace pour de grands volumes.

Quelle est la différence entre L2 et IP ?

L2 est la distance euclidienne. IP est le produit scalaire (Inner Product), utilisé pour les vecteurs normalisés.

Comment gérer la montée en charge ?

Il faut ajouter des Query Nodes supplémentaires. Milvus redistribuera les partitions sur les nouveaux nœuds automatiquement.

Est-ce compatible avec les systèmes legacy ?

Oui, via des bridges Python ou C++. On peut exposer les vecteurs via des API REST standard.

📚 Sur le même blog

🔗 Le même sujet sur nos autres blogs

📝 Conclusion

L’intégration d’une base de données vectorielle dans un écosystème existant demande une rigueur sur la gestion de la mémoire. La performance de Milvus dépend de la configuration fine de ses index HNSW. Surveillez toujours le ratio entre le temps d’indexation et le temps de recherche. Pour approfondir les structures de données, consultez la documentation COBOL officielle. Un index mal dimensionné transformera votre moteur de recherche en un simple parcours de table coûteux.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *