Verwalten des Indexlebenszyklus und Einbetten von Updates

Abgeschlossen

Vektorindizes und Einbettungen erfordern eine fortlaufende Wartung, während sich Ihre Daten entwickeln. Neue Dokumente verschieben die Verteilung von Vektoren, Inhaltsaktualisierungen machen vorhandene Einbettungen ungültig, und Einbettungsmodell-Upgrades erfordern eine Neugenerierung aller Vektoren. Indem Sie verstehen, wie diese Änderungen verwaltet werden können, bleibt Ihre Vektorsuche auf Dauer leistungsfähig.

Diese Einheit umfasst die Überwachung des Indexstatus, das Neuerstellen von Indizes bei Bedarf, das effiziente Aktualisieren von Einbettungen und die Behandlung von Einbettungsmodellmigrationen.

Überwachung der Integrität und Leistung von Indizes

Effektive Wartung beginnt mit dem Verständnis Ihres aktuellen Indexstatus. PostgreSQL bietet Systemansichten und Funktionen, die angeben, ob Indizes verwendet werden, wie viel Speicherplatz sie verbrauchen und ob Abfragen wie erwartet ausgeführt werden.

In der pg_stat_user_indexes Ansicht werden Verwendungsmuster für Ihre Indizes angezeigt. Wenn ein Vektorindex null Scans anzeigt, können Abfragen diesen möglicherweise nicht nutzen – oft, weil die Operatorklasse nicht mit dem Entfernungsoperator in Ihren Abfragen übereinstimmt oder weil der Abfrageplaner für eine kleine Tabelle einen sequenziellen Scan ausgewählt hat. Hohe Scananzahl bestätigt, dass der Index erwartungsgemäß funktioniert.

SELECT
    schemaname,
    relname AS table_name,
    indexrelname AS index_name,
    idx_scan AS times_used,
    pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
FROM pg_stat_user_indexes
WHERE indexrelname LIKE '%embedding%'
ORDER BY idx_scan DESC;

Über Die Verwendungsstatistiken hinaus zeigt die Abfrageleistung den Indexstatus im Laufe der Zeit an. Führen Sie EXPLAIN ANALYZE regelmäßig repräsentative Abfragen aus, und verfolgen Sie die Ausführungszeiten. Wenn Abfragen, die 10 Millisekunden benötigten, jetzt 50 Millisekunden ohne eine entsprechende Erhöhung des Datenvolumens in Anspruch nehmen, hat sich die Indexstruktur möglicherweise verschlechtert. Dies geschieht häufig nach dem Hinzufügen signifikanter neuer Daten, insbesondere aus einer anderen Domäne oder mit unterschiedlichen Merkmalen als dem ursprünglichen Dataset.

Umfangreiche Indexbuilds können Minuten oder Stunden dauern. Überwachen Sie den Fortschritt mithilfe von pg_stat_progress_create_index, das die aktuelle Phase und den Abschlussprozentsatz anzeigt. HNSW entwickelt Fortschritte durch initializing- und loading tuples-Phasen, während IVFFlat zusätzliche Phasen für das K-Means-Clustering zeigt. Der Prozentsatz der Fertigstellung ist während der letzten loading tuples-Phase am sinnvollsten.

Neuerstellen von Indizes, wenn sich die Datenverteilung ändert

ANN-Indizes optimieren ihre internen Strukturen basierend auf der Datenverteilung zur Erstellungszeit. Wenn Sie erhebliche neue Daten hinzufügen, insbesondere Daten aus einer anderen Domäne oder mit unterschiedlichen Merkmalen, kann der Index möglicherweise keine Partitionsvektoren mehr optimal partitionieren. Diese Verschlechterung zeigt sich in einer erhöhten Abfrageverzögerung, einer verringerten Ergebnisqualität oder beidem.

Mehrere Signale deuten darauf hin, dass ein Index neu erstellt werden muss. Die Abfragelatenz erhöht sich ohne eine entsprechende Erhöhung des Datenvolumens, was eine strukturelle Verschlechterung vorschlägt. Benutzer, die weniger relevante Suchergebnisse melden, weisen auf Probleme mit der Erfassungsrate hin. Das Hinzufügen von mehr als 20 bis 30% neuen Daten, insbesondere aus einer neuen Quelle oder Domäne, erfordert häufig eine proaktive Neuindizierung, auch bevor Sie Probleme feststellen.

Verwenden Sie CREATE INDEX CONCURRENTLY für Produktionssysteme, die ausfallzeiten nicht tolerieren können, einen Ersatzindex zu erstellen, ohne Abfragen zu blockieren. Dieser Ansatz dauert länger und verwendet mehr Ressourcen als einen regulären Build, aber Ihre Anwendung bedient weiterhin Anforderungen während des gesamten Prozesses. Erstellen Sie den neuen Index mit einem temporären Namen, vergewissern Sie sich, dass er funktioniert EXPLAIN ANALYZE, legen Sie den alten Index ab, und benennen Sie dann den neuen um, um ihn zu übernehmen.

-- Create new index concurrently (doesn't block queries)
CREATE INDEX CONCURRENTLY documents_embedding_new_idx
ON documents USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);

-- Verify the new index works, then swap
DROP INDEX documents_embedding_idx;
ALTER INDEX documents_embedding_new_idx RENAME TO documents_embedding_idx;

Bei Systemen, die kurze Unterbrechungen tolerieren können, ist REINDEX INDEX documents_embedding_idx eine einfachere Lösung, blockiert jedoch Schreibvorgänge während der Neuerstellung. Planen Sie dies in Zeiten mit geringem Datenverkehr.

Effizientes Aktualisieren von Einbettungen

Wenn sich Der Quellinhalt ändert, stellt die zugeordnete Einbettung die Bedeutung des Inhalts nicht mehr genau dar. Die Synchronisierung von Einbettungen mit Inhalten ist für die Suchqualität von wesentlicher Bedeutung, aber der von Ihnen gewählte Ansatz hängt von den Anforderungen an die Aktualisierungshäufigkeit und Latenz ab.

Das Aktualisieren der Einbettung in derselben Transaktion wie der Inhalt hält bei gelegentlichen Updates alles konsistent. Ihre Anwendung ruft die Einbettungs-API auf, ruft den neuen Vektor ab und schreibt sowohl den Inhalt als auch das Einbetten zusammen. Dieser Ansatz ist einfach und garantiert Konsistenz, fügt aber jeder Inhaltsaktualisierung Latenz hinzu und verknüpft Ihren Inhaltsworkflow eng mit der Verfügbarkeit des Einbettungsdiensts.

Anwendungen mit häufigen Inhaltsänderungen profitieren von einem Batchansatz, der Inhaltsupdates von der Einbettungsgenerierung entkoppelt. Markieren Sie Dokumente so, dass Aktualisierungen mithilfe eines Flagspalten- oder Zeitstempelvergleichs eingebettet werden müssen, und verarbeiten Sie dann ausstehende Updates asynchron in Batches. Dieses Muster reduziert die API-Kosten durch Batchverarbeitung, verbessert die Latenz von Inhaltsupdates und macht Ihr System robuster gegenüber Dienstausfällen beim Einbetten von Diensten.

-- Add a column to track embedding status
ALTER TABLE documents ADD COLUMN embedding_stale BOOLEAN DEFAULT FALSE;

-- When content changes, mark for embedding update (fast)
UPDATE documents
SET content = 'New content...', embedding_stale = TRUE, updated_at = NOW()
WHERE id = 42;

-- Background job processes stale embeddings in batches
SELECT id, content FROM documents WHERE embedding_stale = TRUE LIMIT 100;

-- After generating embeddings externally, update in bulk
UPDATE documents
SET embedding = data.embedding, embedding_stale = FALSE
FROM (VALUES (1, '[...]'::vector), (2, '[...]'::vector)) AS data(id, embedding)
WHERE documents.id = data.id;

Einige Anwendungen profitieren von der regelmäßigen Neugenerierung aller Einbettungen, nicht nur von Inhalten mit Inhaltsänderungen. Dadurch werden subtile Updates abgefangen, die das veraltete Flag nicht auslösen, die Konsistenz für alle Einbettungen sicherstellen und beim Testen verschiedener Einbettungsparameter helfen. Implementieren Sie dies als geplanten Auftrag, der Dokumente verarbeitet, die nach der letzten Aktualisierungszeit sortiert wurden, und zuerst ältere Einbettungen aktualisieren.

Umgang mit Änderungen am Einbettungsmodell

Ein Upgrade auf ein neues Einbettungsmodell oder ein Wechsel des Anbieters erfordert das Neugenerieren aller Einbettungen. Da unterschiedliche Modelle Vektoren mit unterschiedlichen Dimensionen und semantischen Beziehungen erzeugen, können Sie Einbettungen aus verschiedenen Modellen nicht in derselben Spalte kombinieren.

Migrationsstrategie

Für die Migration zu einem neuen Einbettungsmodell ist eine sorgfältige Planung erforderlich, da Vorhandene Einbettungen nicht einfach überschrieben werden können. Ihre Anwendung würde inkonsistente Ergebnisse zurückgeben, während die Migration ausgeführt wird. Der sicherste Ansatz verwendet eine Strategie mit parallelen Spalten, die es ermöglicht, neue Einbettungen neben bestehenden zu erstellen, die Suchqualität zu überprüfen und atomar umzuschalten. Diese Strategie fügt temporären Speicheraufwand hinzu, vermeidet jedoch Ausfallzeiten und ermöglicht es Ihnen, einen Rollback zu ermöglichen, wenn das neue Modell unterperformt.

  1. Fügen Sie eine neue Vektorspalte für die neuen Einbettungen hinzu:

    ALTER TABLE documents ADD COLUMN embedding_v2 vector(3072);
    
  2. Erstellen Eines Indexes für die neue Spalte:

    CREATE INDEX documents_embedding_v2_idx
    ON documents USING hnsw (embedding_v2 vector_cosine_ops);
    
  3. Einbettungen in Chargen auffüllen, um eine Überlastung der Einbettungs-API zu vermeiden:

    -- Process in batches, application code generates embeddings
    UPDATE documents
    SET embedding_v2 = '[...]'::vector
    WHERE id BETWEEN 1 AND 10000
      AND embedding_v2 IS NULL;
    
  4. Aktualisieren Sie Anwendungsabfragen , um die neue Spalte zu verwenden. Sie können beide Spalten während des Tests parallel ausführen:

    -- Compare results from both models
    SELECT id, title,
           embedding <=> $1 AS v1_distance,
           embedding_v2 <=> $2 AS v2_distance
    FROM documents
    ORDER BY embedding_v2 <=> $2
    LIMIT 10;
    
  5. Legen Sie die alte Spalte und den Index nach der Überprüfung der neuen Einbettungen ab:

    DROP INDEX documents_embedding_idx;
    ALTER TABLE documents DROP COLUMN embedding;
    ALTER TABLE documents RENAME COLUMN embedding_v2 TO embedding;
    ALTER INDEX documents_embedding_v2_idx RENAME TO documents_embedding_idx;
    

Geschätzte Migrationszeit

Planen Sie Ihren Migrationszeitplan basierend auf:

  • Zeilenanzahl: Mehr Zeilen bedeuten mehr Einbettung von API-Aufrufen
  • API-Ratenbegrenzungen: Die meisten Einbettungs-APIs haben Anfragen-pro-Minute-Begrenzungen
  • Batchgröße: Größere Batches sind effizienter, können aber zu Zeitüberschreitungen führen.

Bei 500.000 Dokumenten, einer API-Rate von 3.000 Anforderungen pro Minute und Batches von 100 Dokumenten würde das Backfill etwa 28 Stunden dauern.

Verwalten von Speicher und Bereinigung

Vektorspalten verbrauchen erheblichen Speicher. Ein 1536-dimensionaler Vektor verwendet ca. 6 KB pro Zeile. Eine Tabelle mit einer Million Dokumenten und Einbettungen erfordert ca. 6 GB nur für die Vektordaten und zusätzlichen Speicherplatz für Indizes.

Schätzen der Speicheranforderungen

Vektordaten verbrauchen wesentlich mehr Speicher als typische relationale Spalten, sodass die Planungskapazität vor dem Laden großer Datasets Überraschungen verhindert. Wenn Sie die Beziehung zwischen Dimensionsanzahl, Zeilenanzahl und Indexaufwand verstehen, können Sie geeignete Azure-Datenbank für PostgreSQL-Computeebenen und Speicherkonfigurationen auswählen. Sie können vorhandene Tabellen abfragen, um die aktuelle Verwendung zu verstehen und für wachstum zu extrapolieren.

SELECT
    pg_size_pretty(pg_relation_size('documents')) AS table_size,
    pg_size_pretty(pg_indexes_size('documents')) AS index_size,
    pg_size_pretty(pg_total_relation_size('documents')) AS total_size;

Für Planungszwecke schätzen Sie Folgendes:

  • Speicher für Vektorspalten: dimensions * 4 bytes * row_count
  • HNSW-Index-Aufwand: ungefähr 1,5–2 mal die Größe der Vektorspalte
  • IVFFlat-Index-Overhead: ca. 1-1,5x die Größe der Vektorspalte

Speicherplatz nach Updates freigeben

PostgreSQL verwendet ein Multiversion-Parallelitätssteuerungssystem (MVCC), das alte Zeilenversionen behält, bis sie von einer Transaktion nicht mehr benötigt werden. Wenn Sie Zeilen aktualisieren oder löschen, übernimmt PostgreSQL nicht sofort den Speicherplatz. Bei Vektortabellen mit häufigen Aktualisierungen beim Einbetten sammeln sich tote Tupel schneller als typische relationale Tabellen, da jede Einbettungsänderung eine neue Zeilenversion erstellt. Diese Aufblähung kann sich erheblich auf die Leistung und Speicherkosten von Abfragen auswirken, wenn sie nicht proaktiv verwaltet werden. Führen Sie den Befehl aus VACUUM , um Speicherplatz freizufordern:

-- Standard vacuum (runs concurrently)
VACUUM documents;

-- Full vacuum (reclaims more space but locks the table)
VACUUM FULL documents;

Konfigurieren Sie autovacuum für Tabellen mit hoher Aktualisierungsaktivität, um häufiger ausgeführt zu werden:

ALTER TABLE documents SET (
    autovacuum_vacuum_scale_factor = 0.05,
    autovacuum_analyze_scale_factor = 0.02
);

Diese Einstellungen lösen Autovacuum aus, wenn 5% der Zeilen geändert werden, anstatt den Standard von 20%.