Leistung von Vektorabfragen optimieren

In diesem Dokument erfahren Sie, wie Sie Ihre Indexe optimieren, um eine schnellere Abfrageleistung und eine bessere Rückrufleistung zu erzielen.

ScaNN-Index optimieren

Der ScaNN-Index verwendet eine Indexierung, die auf der Quantisierung von Bäumen basiert. Bei der Baumquantisierung lernen Indexe einen Suchbaum zusammen mit einer Quantisierungs- (oder Hash-) Funktion. Wenn Sie eine Abfrage ausführen, wird der Suchbaum verwendet, um den Suchraum zu beschneiden, während die Quantisierung verwendet wird, um die Indexgröße zu komprimieren. Durch diese Beschneidung wird die Bewertung der Ähnlichkeit (d.h. der Distanz) zwischen dem Abfragevektor und den Datenbankvektoren beschleunigt.

Wenn Sie sowohl eine hohe Abfragerate pro Sekunde (QPS) als auch eine hohe Trefferquote bei Abfragen nach dem nächsten Nachbarn erzielen möchten, müssen Sie den Baum Ihres ScaNN-Index so partitionieren, dass er am besten zu Ihren Daten und Abfragen passt.

Bevor Sie einen ScaNN-Index erstellen, müssen Sie Folgendes tun:

  • Achten Sie darauf, dass bereits eine Tabelle mit Ihren Daten erstellt wurde.
  • Achten Sie darauf, dass der Wert, den Sie für das maintenance_work_mem- und das shared_buffers-Flag festlegen, kleiner als der Gesamtarbeitsspeicher des Computers ist, um Probleme beim Generieren des Index zu vermeiden.

Parameter für die Abstimmung

Die folgenden Indexparameter und Datenbankflaggen werden zusammen verwendet, um das richtige Gleichgewicht zwischen Rückruf und QPS zu finden. Alle Parameter gelten für beide ScaNN-Indextypen.

Parameter für die Abstimmung Beschreibung Parametertyp
num_leaves Die Anzahl der Partitionen, die auf diesen Index angewendet werden sollen. Die Anzahl der Partitionen, auf die Sie beim Erstellen eines Index anwenden, wirkt sich auf die Indexleistung aus. Wenn Sie die Partitionen für eine festgelegte Anzahl von Vektoren erhöhen, erstellen Sie einen detaillierteren Index, was die Abruf- und Abfrageleistung verbessert. Dies geht jedoch zu Lasten der Indexerstellungszeit.

Da Drei-Ebenen-Bäume schneller erstellt werden als Zwei-Ebenen-Bäume, können Sie den Wert für num_leaves_value beim Erstellen eines Drei-Ebenen-Baumindexes erhöhen, um eine bessere Leistung zu erzielen.
  • Zweistufiger Index: Legen Sie für diesen Wert einen beliebigen Wert zwischen 1 und 1048576 fest.

    Wenn Sie sich nicht sicher sind, welchen genauen Wert Sie auswählen sollen, verwenden Sie sqrt(ROWS) als Ausgangspunkt. Dabei ist ROWS die Anzahl der Vektorzeilen. Die Anzahl der Vektoren in jeder Partition wird mit
    ROWS/sqrt(ROWS) = sqrt(ROWS) berechnet.

    Da ein zweistufiger Baumindex in einem Dataset mit weniger als 10 Millionen Vektorzeilen erstellt werden kann, enthält jede Partition weniger als (sqrt(10M)) Vektoren, also 3200 Vektoren. Für eine optimale Leistung wird empfohlen, die Anzahl der Vektoren in jeder Partition zu minimieren.
  • Drei-Ebenen-Index: Legen Sie für diesen Wert einen beliebigen Wert zwischen 1 und 1048576 fest.

    Wenn Sie sich nicht sicher sind, welchen genauen Wert Sie auswählen sollen, verwenden Sie power(ROWS, 2/3) als Ausgangspunkt. Dabei ist ROWS die Anzahl der Vektorzeilen. Die Anzahl der Vektoren in jeder Partition wird mit
    ROWS/power(ROWS, 2/3) = power(ROWS, 1/3) berechnet.

    Da ein dreistufiger Baumindex in einem Datensatz mit mehr als 100 Millionen Vektorzeilen erstellt werden kann, enthält jede Partition mehr als
    (power(100M, 1/3)) Vektoren, also 465 Vektoren. Für eine optimale Leistung wird empfohlen, die Anzahl der Vektoren in jeder Partition zu minimieren.
Indexerstellung
quantizer Die Art des Quantisierers, die Sie für den K-Means-Baum verwenden möchten. Der Standardwert ist SQ8 für eine bessere Abfrageleistung.

Legen Sie ihn auf FLAT fest, um die Wahrscheinlichkeit zu erhöhen, dass die Daten abgerufen werden.
Indexerstellung
enable_pca Hiermit wird die Hauptkomponentenanalyse (Principal Component Analysis, PCA) aktiviert. Dabei handelt es sich um eine Dimensionsreduktionstechnik, mit der die Größe der Einbettung nach Möglichkeit automatisch verringert wird. Diese Option ist standardmäßig aktiviert.

Legen Sie false fest, wenn sich die Erinnerungsfähigkeit verschlechtert.
Indexerstellung
scann.num_leaves_to_search Das Datenbankflag steuert den Kompromiss zwischen Trefferquote und QPS. Der Standardwert ist 1% des in num_leaves festgelegten Werts.

Je höher der festgelegte Wert, desto besser ist der Recall, aber die QPS ist niedriger. Umgekehrt gilt das Gleiche.
Ausführungszeit der Abfrage
scann.max_top_neighbors_buffer_size Mit dem Datenbankflag wird die Größe des Caches angegeben, der verwendet wird, um die Leistung für gefilterte Abfragen zu verbessern. Dazu werden die gescannten Kandidatennachbarn im Arbeitsspeicher anstelle auf der Festplatte bewertet oder sortiert. Der Standardwert ist 20000.

Je höher der festgelegte Wert ist, desto höher ist die Anzahl der Abfragen pro Sekunde bei gefilterten Abfragen. Dies führt jedoch zu einer höheren Arbeitsspeichernutzung und umgekehrt.
Ausführungszeit der Abfrage
scann.pre_reordering_num_neighbors Wenn das Datenbankflag festgelegt ist, gibt es an, wie viele Kandidaten in den Neusortierungsphasen berücksichtigt werden sollen, nachdem bei der ersten Suche eine Reihe von Kandidaten ermittelt wurde. Legen Sie einen Wert fest, der höher ist als die Anzahl der Nachbarn, die durch die Abfrage zurückgegeben werden sollen.

Größere Wertemengen führen zu einer besseren Trefferquote, aber dieser Ansatz führt zu einer geringeren QPS.
Ausführungszeit der Abfrage
max_num_levels Die maximale Anzahl von Ebenen des K-Means-Clustering-Baums.
  • Zweistufiger Baumindex: Standardmäßig für die zweistufige treebasierte Quantisierung festgelegt.
  • Dreistufiger Baumindex: Legen Sie für die dreistufige treebasierte Quantisierung explizit 2 fest.
Indexerstellung

ScaNN-Index optimieren

In den folgenden Beispielen für ScaNN-Indexe mit zwei und drei Ebenen wird gezeigt, wie die Optimierungsparameter festgelegt werden:

Zweistufiger Index

SET LOCAL scann.num_leaves_to_search = 1;
SET LOCAL scann.pre_reordering_num_neighbors=50;

CREATE INDEX my-scann-index ON my-table
  USING scann (vector_column cosine)
  WITH (num_leaves = [power(1000000, 1/2)]);

Dreistufiger Index

SET LOCAL scann.num_leaves_to_search = 10;
SET LOCAL scann.pre_reordering_num_neighbors=50;

CREATE INDEX my-scann-index ON my-table
  USING scann (vector_column cosine)
  WITH (num_leaves = [power(1000000, 2/3)], max_num_levels = 2);

Alle Einfüge- oder Aktualisierungsvorgänge für eine Tabelle, für die bereits ein ScaNN-Index generiert wurde, wirken sich darauf aus, wie der gelernte Baum den Index optimiert. Wenn Ihre Tabelle häufig aktualisiert oder Daten eingefügt werden, empfehlen wir, den vorhandenen ScaNN-Index regelmäßig neu zu indexieren, um die Abrufgenauigkeit zu verbessern.

Sie können Indexmesswerte überwachen, um die Anzahl der Mutationen zu ermitteln, die seit dem Erstellen des Index erstellt wurden, und dann entsprechend neu indexieren. Weitere Informationen zu Messwerten finden Sie unter Messwerte für Vektorindexe.

Best Practices für die Optimierung

Je nachdem, welchen ScaNN-Index Sie verwenden möchten, variieren die Empfehlungen zur Indexoptimierung. In diesem Abschnitt finden Sie Empfehlungen zur Optimierung der Indexparameter für ein optimales Gleichgewicht zwischen Rückruf und QPS.

Zweistufiger Baumindex

So wenden Sie Empfehlungen an, um die optimalen Werte für num_leaves und num_leaves_to_search für Ihren Datensatz zu ermitteln:

  1. Erstellen Sie den ScaNN-Index mit num_leaves als Quadratwurzel der Zeilenanzahl der indexierten Tabelle.
  2. Führen Sie die Testabfragen aus und erhöhen Sie den Wert von scann.num_of_leaves_to_search, bis Sie den gewünschten Bereich für die Wiedererkennung erreichen, z. B. 95%. Weitere Informationen zur Analyse von Abfragen finden Sie unter Abfragen analysieren.
  3. Notieren Sie sich das Verhältnis zwischen scann.num_leaves_to_search und num_leaves, das in den nächsten Schritten verwendet wird. Dieses Verhältnis liefert eine Näherung an den Datensatz, mit der Sie die gewünschte Wiedererkennung erzielen.

    Wenn Sie mit Vektoren mit hoher Dimension (500 Dimensionen oder mehr) arbeiten und die Wiedererkennung verbessern möchten, können Sie den Wert von scann.pre_reordering_num_neighbors anpassen. Legen Sie als Ausgangspunkt den Wert auf 100 * sqrt(K) fest, wobei K das Limit ist, das Sie in Ihrer Abfrage festgelegt haben.
  4. Wenn die QPS zu niedrig ist, nachdem Ihre Abfragen die gewünschte Trefferquote erreicht haben, gehen Sie so vor:
    1. Erstellen Sie den Index neu und erhöhen Sie die Werte von num_leaves und scann.num_leaves_to_search gemäß der folgenden Anleitung:
      • Legen Sie für num_leaves einen größeren Faktor der Quadratwurzel der Zeilenanzahl fest. Wenn num_leaves im Index beispielsweise auf die Quadratwurzel der Zeilenanzahl festgelegt ist, versuchen Sie, den Wert auf das Doppelte der Quadratwurzel zu setzen. Wenn der Wert bereits doppelt so hoch ist, versuchen Sie, ihn auf das Dreifache der Quadratwurzel festzulegen.
      • Erhöhen Sie scann.num_leaves_to_search nach Bedarf, um das Verhältnis zu num_leaves beizubehalten, das Sie in Schritt 3 notiert haben.
      • Legen Sie für num_leaves einen Wert fest, der kleiner oder gleich der Anzahl der Zeilen geteilt durch 100 ist.
    2. Führen Sie die Testabfragen noch einmal aus. Versuchen Sie während der Ausführung der Testabfragen, scann.num_leaves_to_search zu reduzieren, um einen Wert zu finden, mit dem die Anzahl der Abfragen pro Sekunde erhöht wird, während die Trefferquote hoch bleibt. Probieren Sie verschiedene Werte für scann.num_leaves_to_search aus, ohne den Index neu zu erstellen.
  5. Wiederholen Sie Schritt 4, bis sowohl die QPS als auch der Rückrufbereich akzeptable Werte erreicht haben.

Dreistufiger Baumindex

Zusätzlich zu den Empfehlungen für den zweistufigen BaumScaNN-Index können Sie die folgenden Hinweise und Schritte zur Optimierung des Index verwenden:

  • Wenn Sie max_num_levels von 1 für einen zweistufigen Baum auf 2 für einen dreistufigen Baum erhöhen, verkürzt sich die Zeit zum Erstellen eines Index erheblich. Dies geht jedoch zu Lasten der Recall-Genauigkeit. Legen Sie max_num_levels mithilfe der folgenden Empfehlung fest:
    • Legen Sie den Wert auf 2 fest, wenn die Anzahl der Vektorzeilen 100 Millionen überschreitet.
    • Legen Sie den Wert auf 1 fest, wenn die Anzahl der Vektorzeilen weniger als 10 Millionen beträgt.
    • Legen Sie entweder 1 oder 2 fest, wenn die Anzahl der Vektorzeilen zwischen 10 Millionen und 100 Millionen liegt. Berücksichtigen Sie dabei die Zeit für die Indexerstellung und die erforderliche Abrufgenauigkeit.

So wenden Sie Empfehlungen an, um den optimalen Wert für die Indexparameter num_leaves und max_num_levels zu ermitteln:

  1. Erstellen Sie den ScaNN-Index mit den folgenden num_leaves- und max_num_levels-Kombinationen basierend auf Ihrem Datenpool:

    • Vector Rows-Zeilen mit mehr als 100 Millionen Zeilen: Legen Sie max_num_levels als 2 und num_leaves als power(rows, ⅔) fest.
    • Weniger als 100 Millionen Vektorzeilen: Legen Sie max_num_levels als 1 und num_leaves als sqrt(rows) fest.
    • Vektorzeilen zwischen 10 Millionen und 100 Millionen Zeilen: Legen Sie zuerst max_num_levels als 1 und num_leaves als sqrt(rows) fest.
  2. Führen Sie Ihre Testabfragen aus. Weitere Informationen zum Analysieren von Abfragen finden Sie unter Abfragen analysieren.

    Wenn die Indexerstellungszeit zufriedenstellend ist, belassen Sie den Wert für max_num_levels und experimentieren Sie mit dem Wert für num_leaves, um die Genauigkeit der Wiedererkennung zu optimieren.

  3. Wenn Sie mit der Zeit für die Indexerstellung nicht zufrieden sind, gehen Sie so vor:

    • Wenn der Wert von max_num_levels 1 ist, löschen Sie den Index. Erstellen Sie den Index neu, wobei der Wert für max_num_levels auf 2 festgelegt ist.

      Führen Sie die Abfragen aus und optimieren Sie den Wert für num_leaves, um eine optimale Trefferquote zu erzielen.

    • Wenn der Wert für max_num_levels 2 ist, löschen Sie den Index. Erstellen Sie den Index mit demselben max_num_levels-Wert neu und optimieren Sie den num_leaves-Wert für eine optimale Abrufgenauigkeit.

IVF-Index optimieren

Durch die Optimierung der Werte, die Sie für die Parameter lists, ivf.probes und quantizer festgelegt haben, lässt sich die Leistung Ihrer Anwendung möglicherweise optimieren:

Parameter für die Abstimmung Beschreibung Parametertyp
lists Die Anzahl der Listen, die beim Erstellen des Index erstellt wurden. Der Ausgangspunkt für die Festlegung dieses Werts ist (rows)/1000 für bis zu eine Million Zeilen und sqrt(rows) für mehr als eine Million Zeilen. Indexerstellung
quantizer Die Art des Quantisierers, die Sie für den K-Means-Baum verwenden möchten. Der Standardwert ist SQ8 für eine bessere Abfrageleistung. Legen Sie FLAT fest, um sich besser daran zu erinnern. Indexerstellung
ivf.probes die Anzahl der Listen, die bei der Suche angezeigt werden sollen. Der Ausgangspunkt für diesen Wert ist
sqrt(lists).
Ausführungszeit der Abfrage

Im folgenden Beispiel wird ein IVF-Index mit den eingestellten Optimierungsparametern gezeigt:

SET LOCAL ivf.probes = 10;

CREATE INDEX my-ivf-index ON my-table
  USING ivf (vector_column cosine)
  WITH (lists = 100, quantizer = 'SQ8');

IVFFlat-Index optimieren

Durch die Optimierung der Werte, die Sie für die Parameter lists und ivfflat.probes festgelegt haben, lässt sich die Anwendungsleistung verbessern:

Parameter für die Abstimmung Beschreibung Parametertyp
lists Die Anzahl der Listen, die beim Erstellen des Index erstellt wurden. Der Ausgangspunkt für die Festlegung dieses Werts ist (rows)/1000 für bis zu eine Million Zeilen und sqrt(rows) für mehr als eine Million Zeilen. Indexerstellung
ivfflat.probes Die Anzahl der Listen, die bei der Suche untersucht werden sollen. Der Ausgangspunkt für diesen Wert ist
sqrt(lists).
Ausführungszeit der Abfrage

Bevor Sie einen IVFFlat-Index erstellen, muss das IVFFlat-Flag Ihrer Datenbank auf einen Wert festgelegt sein, der die Indexerstellung in großen Tabellen beschleunigt.max_parallel_maintenance_workers

Im folgenden Beispiel wird ein IVFFlat-Index mit den eingestellten Optimierungsparametern gezeigt:

SET LOCAL ivfflat.probes = 10;

CREATE INDEX my-ivfflat-index ON my-table
  USING ivfflat (vector_column cosine)
  WITH (lists = 100);

HNSW-Index optimieren

Durch die Optimierung der Werte, die Sie für die Parameter m, ef_construction und hnsw.ef_search festgelegt haben, lässt sich die Anwendungsleistung verbessern.

Parameter für die Abstimmung Beschreibung Parametertyp
m Die maximale Anzahl von Verbindungen pro Knoten im Graphen. Sie können mit dem Standardwert 16(Standard) beginnen und je nach Größe des Datensatzes mit höheren Werten experimentieren. Indexerstellung
ef_construction Die Größe der dynamischen Kandidatenliste, die während der Graphenkonstruktion verwaltet wird und die aktuellen besten Kandidaten für die nächsten Nachbarn eines Knotens ständig aktualisiert. Legen Sie diesen Wert auf einen Wert fest, der mehr als doppelt so hoch wie der Wert für m ist, z. B. 64(Standard). Indexerstellung
ef_search Die Größe der dynamischen Kandidatenliste, die bei der Suche verwendet wird. Sie können diesen Wert entweder auf m oder ef_construction festlegen und dann ändern, während Sie den Abruf beobachten. Der Standardwert ist 40. Ausführungszeit der Abfrage

Im folgenden Beispiel wird ein hnsw-Index mit den eingestellten Optimierungsparametern gezeigt:

SET LOCAL hnsw.ef_search = 40;

CREATE INDEX my-hnsw-index ON my-table
  USING hnsw (vector_column cosine)
  WITH (m = 16, ef_construction = 200);

Abfragen analysieren

Verwenden Sie den Befehl EXPLAIN ANALYZE, um Ihre Abfragestatistiken zu analysieren, wie in der folgenden Beispiel-SQL-Abfrage gezeigt.

  EXPLAIN ANALYZE SELECT result-column FROM my-table
    ORDER BY EMBEDDING_COLUMN ::vector
    USING INDEX my-scann-index
    <-> embedding('textembedding-gecko@003', 'What is a database?')
    LIMIT 1;

Die Beispielantwort QUERY PLAN enthält Informationen wie die benötigte Zeit, die Anzahl der gescannten oder zurückgegebenen Zeilen und die verwendeten Ressourcen.

Limit  (cost=0.42..15.27 rows=1 width=32) (actual time=0.106..0.132 rows=1 loops=1)
  ->  Index Scan using my-scann-index on my-table  (cost=0.42..858027.93 rows=100000 width=32) (actual time=0.105..0.129 rows=1 loops=1)
        Order By: (embedding_column <-> embedding('textgecko@003', 'What is a database?')::vector(768))
        Limit value: 1
Planning Time: 0.354 ms
Execution Time: 0.141 ms

Messwerte für Vektorindexe ansehen

Mit den Messwerten für Vektorindexe können Sie die Leistung Ihres Vektorindex prüfen, Verbesserungsmöglichkeiten ermitteln und den Index bei Bedarf anhand der Messwerte optimieren.

Wenn Sie alle Messwerte für den Vektorindex aufrufen möchten, führen Sie die folgende SQL-Abfrage aus, in der die Ansicht pg_stat_ann_indexes verwendet wird:

SELECT * FROM pg_stat_ann_indexes;

Eine vollständige Liste der Messwerte finden Sie unter Messwerte für Vektorindexe.

Nächste Schritte