Freigeben über


Konfigurieren eines Auslastungstests für Vektorsuchendpunkte

Diese Seite enthält Anleitungen, Beispielcode und ein Notebook-Beispiel für den Lasttest von Vektorsuchendpunkten. Lasttests helfen Ihnen, die Leistung und Einsatzbereitschaft eines Vektor-Suchendpunkts zu verstehen, bevor er in die Produktion bereitgestellt wird. Lasttests können Ihnen Folgendes mitteilen:

  • Latenz auf unterschiedlichen Skalierungsebenen
  • Durchsatzgrenzwerte und Engpässe (Anforderungen pro Sekunde, Latenzaufschlüsselung)
  • Fehlerraten bei anhaltender Last
  • Ressourcenauslastung und Kapazitätsplanung

Weitere Informationen zu Auslastungstests und verwandten Konzepten finden Sie unter Auslastungstests für das Bereitstellen von Endpunkten.

Anforderungen

Bevor Sie diese Schritte starten, müssen Sie über einen implementierten Vektorsuche-Endpunkt und einen Dienstprinzipal mit 'Can Query'-Berechtigungen für den Endpunkt verfügen. Siehe Schritt 1: Einrichten der Dienstprinzipalauthentifizierung.

Laden Sie eine Kopie der folgenden Dateien und Beispielnotizbücher in Ihren Azure Databricks-Arbeitsbereich herunter, und importieren Sie sie:

  • input.json. Dies ist ein Beispiel für die input.json Datei, die die Nutzlast angibt, die von allen gleichzeitigen Verbindungen mit Ihrem Endpunkt gesendet wird. Sie können bei Bedarf mehrere Dateien haben. Wenn Sie das Beispielnotizbuch verwenden, wird diese Datei automatisch aus der bereitgestellten Eingabetabelle generiert.
  • fast_vs_load_test_async_load.py. Laden Sie dieses Skript in Ihren Arbeitsbereich hoch (z. B /Workspace/Users/<your-username>/fast_vs_load_test_async_load.py. ), und legen Sie den locust_script_path Notizbuchparameter auf seinen Pfad fest. Dieses Skript behandelt die Authentifizierung, Nutzlastübermittlung und Debugmetrikensammlung.
  • Das folgende Beispielnotizbuch, das die Auslastungstests ausführt. Um eine optimale Leistung zu erzielen, führen Sie dieses Notizbuch auf einem Einzelknotencluster mit einer großen Anzahl von Kernen aus (Locust skaliert über alle verfügbaren CPUs). Hoher Arbeitsspeicher wird für Abfragen mit vorab generierten Einbettungen empfohlen.

Beispiel-Notebook und Schnellstart

Verwenden Sie das folgende Beispielnotizbuch, um zu beginnen. Es unterstützt zwei Explorationsmodi: eine schrittweise Suche, die bestimmte von Ihnen definierte Parallelitätsebenen testet, und einen binären Suchmodus, der automatisch die maximal tragbare QPS in einigen Schritten findet. Alle Parameter werden mithilfe von Widgets konfiguriert, sodass das Notizbuch interaktiv oder als Databricks-Auftrag ohne Codebearbeitung ausgeführt werden kann.

Notebook für Lastentest mit Locust

Notebook abrufen

Ladentestframework: Locust

Locust ist ein Open-Source-Lastentestframework, mit dem Sie folgende Aktionen ausführen können:

  • Variieren der Anzahl der gleichzeitigen Clientverbindungen
  • Steuern, wie schnell Verbindungen entstehen
  • Messen der Endpunktleistung im gesamten Test
  • Automatische Erkennung und Verwendung aller verfügbaren CPU-Kerne

Das Beispielnotizbuch verwendet das --processes -1 Kennzeichen, um CPU-Kerne automatisch zu erkennen und vollständig zu nutzen.

Wenn Locust durch die CPU eine Engstelle bildet, wird eine Meldung in der Ausgabe angezeigt.

Schritt 1: Einrichten der Dienstprinzipalauthentifizierung

Von Bedeutung

Verwenden Sie für produktionsähnliche Leistungstests immer die OAuth-Dienstprinzipalauthentifizierung. Dienstprinzipale bieten bis zu 100 ms schnellere Reaktionszeiten und höhere Anforderungsratenlimits im Vergleich zu Persönlichen Zugriffstoken (PATs).

Erstellen und Konfigurieren des Dienstprinzipals

  1. Erstellen Sie einen Databricks-Dienstprinzipal. Anweisungen finden Sie unter Hinzufügen von Dienstprinzipalen zu Ihrem Konto.

  2. Berechtigungen erteilen:

    • Navigieren Sie zur Vektor-Suchendpunkt-Seite.
    • Klicken Sie auf Berechtigungen.
    • Erteilen Sie dem Dienstprinzipal Can Query-Berechtigungen.
  3. OAuth-Schlüssel erstellen.

    • Wechseln Sie zur Seite "Dienstprinzipaldetails".
    • Klicke auf die Registerkarte Geheimnisse.
    • Klicken Sie auf "Geheimen Schlüssel generieren".
    • Festlegen der Lebensdauer (empfehlen 365 Tage für Langzeittests).
    • Kopieren Sie die Client-ID und den geheimen Schlüssel sofort.
  4. Speichern Sie Anmeldeinformationen sicher.

    # In a Databricks notebook
    dbutils.secrets.put("load-test-auth", "service_principal_client_id", "<CLIENT_ID>")
    dbutils.secrets.put("load-test-auth", "service_principal_client_secret", "<SECRET>")
    

Schritt 2: Konfigurieren des Auslastungstests

Laptop-Konfiguration

Konfigurieren Sie die Notizbuchparameter mithilfe der Widgets am oberen Rand des Notizbuchs. Wenn Sie das Notizbuch als Databricks-Auftrag ausführen, übergeben Sie diese Werte als Auftragsparameter. Es sind keine Codebearbeitungen erforderlich.

Parameter Description Empfohlener Wert
endpoint_name Name Ihres Vektorsuchendpunkts Ihr Endpunktname
index_name Vollständiger Indexname (catalog.schema.index) Ihr Indexname
test_table Abfragen aus der Quelltabelle erstellen (catalog.schema.table) Ihre Indexeingabetabelle
query_column Textspalte, die für verwaltete Einbettungen verwendet werden soll text Lassen oder auf den eigenen Spaltennamen festlegen
embedding_column Spalte mit vorkompilierten Einbettungsvektoren. Wird nur für selbstverwaltete Einbettungen verwendet. Leer lassen für verwaltete Einbettungen
sample_size Anzahl der Abfragen, die für den Test getestet werden sollen 1000
target_concurrencies Durch Trennzeichen getrennte Liste der zu testenden gleichzeitigen Clientanzahlen 5,10,20,50
step_duration_seconds Dauer in Sekunden pro Parallelitätsebene. Ein Wert gilt für alle Ebenen oder stellt eine pro Ebene als durch Trennzeichen getrennte Liste bereit. 300 (5 Minuten)
secret_scope_name Name des Databricks-Geheimnisbereichs Ihr Bereichsname
locust_script_path Arbeitsbereichspfad zum fast_vs_load_test_async_load.py Skript /Workspace/Users/<your-username>/fast_vs_load_test_async_load.py
output_table (Optional) Delta-Tabelle zum Speichern der Ergebnisse in (catalog.schema.table). Automatisch bei der ersten Ausführung erstellt. catalog.schema.load_test_results
run_name Benennen oder Kommentieren, um diese Ausführung für eine spätere Analyse zu markieren Eine beschreibende Bezeichnung
exploration_mode gradual durchläuft target_concurrencies in Reihenfolge. binary_search findet den Bruchpunkt automatisch (siehe Durchbruchpunkterkundung). gradual
max_target_qps (binary_search nur) Obere Grenze für die QPS-Suche 500
exploration_steps (binary_search nur) Maximale Anzahl binärer Suchdurchläufe 8
error_rate_threshold (binary_search nur) Maximale zulässige Fehlerrate (%) für einen Schritt, der als Erfolg gezählt werden soll 1.0
num_results Anzahl der Ergebnisse, die pro Abfrage zurückgegeben werden sollen 10
columns_to_return Durch Trennzeichen getrennte Liste von Spalten, die in Abfrageergebnissen zurückgegeben werden sollen (z. B id,text. ). Lassen Sie das Feld leer, um alle Spalten zurückzugeben. Leer lassen für Standardwert

Verwaltete und selbstverwaltete Einbettungen

Das Notizbuch unterstützt sowohl verwaltete Einbettungen (wobei Databricks Einbettungen zur Abfragezeit generiert) als auch selbstverwaltete Einbettungen (bei denen Sie vorkompilierte Vektoren direkt übergeben). Konfigurieren Sie die entsprechenden Parameter basierend auf Ihrem Indextyp.

Indextyp Festzulegende Parameter Nicht festlegen
Verwaltete Einbettungen (Delta-Sync-Index mit von Databricks verwaltetem Einbettungsmodell) query_column — der Textspaltenname, der als Abfrage verwendet werden soll embedding_column (Leer lassen)
Selbstverwaltete Einbettungen (Delta-Synchronisierungs- oder Direct Vector Access-Index mit vorkompilierten Vektoren) embedding_column — die Spalte mit vorkompilierten Einbettungsvektoren query_column

Hinweis

Bei verwalteten Einbettungsindizes misst der Lasttest die End-to-End-Latenz einschließlich der Einbettungszeit. Wenn der Einbettungsendpunkt auf Null skaliert wird, wird der Kaltstart-Overhead in der ersten Testausführung angezeigt. Informationen zum Isolieren der Einbettungslatenz von der Suchlatenz finden Sie unter Identifizieren des Einbettungsmodellengpässes .

Warum 5-10 Minuten?

Eine mindeste Testdauer von 5 Minuten ist kritisch.

  • Anfangsabfragen können eine Startzeitverzögerung beinhalten.
  • Endpunkte benötigen Zeit, um die Leistung des stabilen Zustands zu erreichen.
  • Die automatische Skalierung der Bereitstellung von Modellendpunkten (falls aktiviert) benötigt Zeit, um wirksam zu werden.
  • Kurze Tests verfehlen Drosselungsverhalten unter anhaltender Last.

In der folgenden Tabelle sind die empfohlenen Testdauern je nach Testziel aufgeführt.

Testtyp Testdauer Ziele des Tests
Schneller Rauchtest 2-3 Minuten Überprüfen der grundlegenden Funktionalität
Leistungsbasislinie 5–10 Minuten Zuverlässige Metriken für beständigen Zustand
Stresstest 15-30 Minuten Identifizieren der Ressourcenausschöpfung
Ausdauertests 1-4 Stunden Beeinträchtigung, Latenzstabilität

Durchsuchen von Unterbrechungspunkten (Binärsuchmodus)

Zusätzlich zum graduellen Aufräumen (exploration_mode=gradual) unterstützt das Notizbuch einen automatischen binären Suchmodus, der den maximalen nachhaltigen QPS findet, ohne dass Sie die Parallelitätsebenen manuell angeben müssen.

So funktioniert es

Legen Sie exploration_mode=binary_search fest und spezifizieren Sie max_target_qps (z. B. 500). Das Notizbuch verwendet Littles Gesetz (concurrency = QPS × avg_latency_sec), um jedes QPS-Ziel in ein geschätztes Parallelitätsniveau zu konvertieren, und führt dann eine binäre Suche wie folgt durch:

  1. Beginnen Sie bei max_target_qps / 2 (250 im Beispiel).
  2. Wenn die Fehlerrate unter der Erfolgsgrenze (error_rate_threshold) liegt, erhöhen Sie die untere Schwelle und versuchen Sie es mit einem höheren Abfragevolumen pro Sekunde (zuerst 375, dann 500 usw.).
  3. Wenn die Fehlerrate den Schwellenwert (Misserfolg) überschreitet, senken Sie die obere Grenze und versuchen Sie es in der Mitte zwischen dem letzten Erfolg und Misserfolg.
  4. Wiederholen Sie diesen Vorgang bis zu exploration_steps Schritte (Standard 8) oder bis der Suchbereich auf 5 % von max_target_qps eingegrenzt ist.

Die folgende Tabelle zeigt, wie die Suche für einen hypothetischen Endpunkt mit einem Bruchpunkt um 430 QPS konvergiert:

Schritt Ziel-QPS Fehlerrate Ergebnis Neuer Bereich
1 250 0.1% ERFOLG [250, 500]
2 375 0.3% ERFOLG [375, 500]
3 437 4.5% AUSFALL [375, 437]
4 406 0,8 % ERFOLG [406, 437]
5 421 2.1% FEHLER [406, 421]

Nach 5 bis 8 Schritten konvergiert die Suche auf den Bruchpunkt – in diesem Beispiel etwa 406–421 QPS – mit weit weniger Testläufen als eine erschöpfende Suche.

Wann jeder Modus verwendet werden soll

Modus Wann verwenden?
gradual Sie kennen bereits den erwarteten Betriebsbereich und möchten die Leistung auf bestimmten Parallelitätsebenen charakterisieren.
binary_search Sie möchten die maximal nachhaltigen QPS schnell finden, ohne die Parallelitätsstufen im Voraus zu kennen.

Schritt 3. Entwerfen des Abfragesatzes

Wenn möglich, sollte der Abfragesatz den erwarteten Produktionsdatenverkehr so genau wie möglich widerspiegeln. Insbesondere sollten Sie versuchen, die erwartete Verteilung von Abfragen hinsichtlich Inhalt, Komplexität und Vielfalt abzugleichen.

  • Verwenden Sie realistische Abfragen. Verwenden Sie keinen zufälligen Text wie "Testabfrage 1234".

  • Entspricht der erwarteten Verteilung des Produktionsverkehrs. Wenn Sie 80% allgemeine Abfragen, 15% abfragen mittlerer Häufigkeit und 5% seltenen Abfragen erwarten, sollte ihr Abfragesatz diese Verteilung widerspiegeln.

  • Passen Sie den Abfragetyp an, den Sie im Betrieb erwarten. Wenn Sie z. B. erwarten, dass Produktionsabfragen hybride Such- oder Filter verwenden, sollten Sie diese auch in Ihrem Abfragesatz verwenden.

    Beispielabfrage mit Filtern:

    {
      "query_text": "wireless headphones",
      "num_results": 10,
      "filters": { "brand": "Sony", "noise_canceling": true }
    }
    

    Beispielabfrage mithilfe der Hybridsuche:

    {
      "query_text": "best noise canceling headphones for travel",
      "query_type": "hybrid",
      "num_results": 10
    }
    

Abfragevielfalt und Zwischenspeicherung

Vektorsuchendpunkte speichern mehrere Arten von Abfrageergebnissen zwischen, um die Leistung zu verbessern. Diese Zwischenspeicherung kann sich auf Die Ergebnisse des Ladetests auswirken. Aus diesem Grund ist es wichtig, auf die Vielfalt des Abfragesatzes zu achten. Wenn Sie z. B. wiederholt dieselbe Gruppe von Abfragen senden, testen Sie den Cache, nicht die tatsächliche Suchleistung.

Verwendung: Wenn: Example
Identische oder wenige Abfragen
  • Ihr Produktionsdatenverkehr hat eine hohe Abfragewiederholung (z. B. "beliebte Produkte")
  • Sie testen die Cacheeffektivität speziell
  • Ihre Anwendung profitiert von der Zwischenspeicherung (z. B. Dashboards mit festen Abfragen)
  • Sie möchten die Leistung des Zwischenspeichers im Bestfall messen.
Ein Produktempfehlungs-Widget, das "trending items" anzeigt – die gleiche Abfrage wird tausende mal pro Stunde ausgeführt.
Verschiedene Abfragen
  • Ihr Produktionsdatenverkehr hat eindeutige Benutzerabfragen (z. B. Suchmaschinen oder Chatbots)
  • Sie möchten die Leistung im nicht zwischengespeicherten Fall messen
  • Sie möchten die Leistung des Indexscans testen, nicht die Cacheleistung
  • Abfragen weisen eine hohe Kardinalität auf (Millionen möglicher Variationen)
Eine E-Commerce-Suche, bei der jeder Benutzer verschiedene Produktsuchen eingibt.

Weitere Empfehlungen finden Sie unter "Zusammenfassung der bewährten Methoden".

Optionen zum Erstellen eines Abfragesatzes

Auf den Code-Tabs werden drei Optionen zum Erstellen eines vielfältigen Abfragesatzes angezeigt. Es gibt keine Einheitslösung. Wählen Sie das für Sie am besten geeignete aus.

  • (Empfohlen) Zufällige Stichprobenziehung aus der Eingabetabelle des Index. Dies ist ein guter allgemeiner Ausgangspunkt.
  • Sampling aus Produktionsprotokollen. Dies ist ein guter Start, wenn Sie Über Produktionsprotokolle verfügen. Denken Sie daran, dass Abfragen in der Regel im Laufe der Zeit geändert werden. Aktualisieren Sie daher den Testsatz regelmäßig, um ihn auf dem neuesten Stand zu halten.
  • Generieren synthetischer Abfragen. Dies ist hilfreich, wenn Sie nicht über Produktionsprotokolle verfügen oder komplexe Filter verwenden.

Zufälliges Sampling aus der Eingabetabelle

In den folgenden Codebeispielen werden zufällige Abfragen aus der Eingabetabelle des Indexes entnommen.

import pandas as pd
import random

# Read the index input table
input_table = spark.table("catalog.schema.index_input_table").toPandas()

# Sample random rows
n_samples = 1000
if len(input_table) < n_samples:
    print(f"Warning: Only {len(input_table)} rows available, using all")
    sample_queries = input_table
else:
    sample_queries = input_table.sample(n=n_samples, random_state=42)

# Extract the text column (adjust column name as needed)
queries = sample_queries['text_column'].tolist()

# Create query payloads
query_payloads = [{"query_text": q, "num_results": 10} for q in queries]

# Save to input.json
pd.DataFrame(query_payloads).to_json("input.json", orient="records", lines=True)

print(f"Created {len(query_payloads)} diverse queries from index input table")

Beispiel aus Produktionsprotokollen

Die folgenden Codebeispiele stammen proportional aus Produktionsabfragen.

# Sample proportionally from production queries
production_queries = pd.read_csv("queries.csv")

# Take stratified sample maintaining frequency distribution
def create_test_set(df, n_queries=1000):
    # Group by frequency buckets
    df['frequency'] = df.groupby('query_text')['query_text'].transform('count')

    # Stratified sample
    high_freq = df[df['frequency'] > 100].sample(n=200)  # 20%
    med_freq = df[df['frequency'].between(10, 100)].sample(n=300)  # 30%
    low_freq = df[df['frequency'] < 10].sample(n=500)  # 50%

    return pd.concat([high_freq, med_freq, low_freq])

test_queries = create_test_set(production_queries)
test_queries.to_json("input.json", orient="records", lines=True)

Synthetische Abfragen

Wenn Sie noch keine Produktionsprotokolle haben, können Sie synthetische verschiedene Abfragen generieren.

# Generate diverse queries programmatically
import random

# Define query templates and variations
templates = [
    "find {product} under ${price}",
    "best {product} for {use_case}",
    "{adjective} {product} recommendations",
    "compare {product1} and {product2}",
]

products = ["laptop", "headphones", "monitor", "keyboard", "mouse", "webcam", "speaker"]
prices = ["500", "1000", "1500", "2000"]
use_cases = ["gaming", "work", "travel", "home office", "students"]
adjectives = ["affordable", "premium", "budget", "professional", "portable"]

diverse_queries = []
for _ in range(1000):
    template = random.choice(templates)
    query = template.format(
        product=random.choice(products),
        product1=random.choice(products),
        product2=random.choice(products),
        price=random.choice(prices),
        use_case=random.choice(use_cases),
        adjective=random.choice(adjectives)
    )
    diverse_queries.append(query)

print(f"Generated {len(set(diverse_queries))} unique queries")

Schritt 4. Testen Sie Ihre Nutzlast

Überprüfen Sie vor dem Ausführen des Vollständigen Auslastungstests Ihre Nutzlast:

  1. Navigieren Sie im Arbeitsbereich "Databricks" zu Ihrem Vektorsuchendpunkt.
  2. Klicken Sie in der linken Randleiste auf Dienste.
  3. Wählen Sie Ihren Endpunkt aus.
  4. Klicken Sie auf VerwendenAbfrage.
  5. Fügen Sie Ihren input.json Inhalt in das Abfragefeld ein.
  6. Überprüfen Sie, ob der Endpunkt erwartete Ergebnisse zurückgibt.

Dadurch wird sichergestellt, dass ihr Auslastungstest realistische Abfragen misst und keine Fehlerantworten.

Schritt 5. Auslastungstest ausführen

Verbindungsüberprüfung und -warmup

Bevor der Ladetest beginnt, führt das Notizbuch zwei Setupschritte aus:

  1. Konnektivitätsprüfung: Sendet eine einzelne Probeabfrage unter Verwendung der Anmeldeinformationen des Diensthauptbenutzers. Wenn der Endpunkt einen 401- oder 403-Fehler zurückgibt, schlägt das Notebook sofort mit einem deutlichen PermissionError fehl, statt einen vollständigen Belastungstest auszuführen, der nur Fehlerdaten erzeugt. Dies spart Zeit, wenn Anmeldeinformationen oder Berechtigungen falsch konfiguriert sind.

  2. Warmuptest (1 Minute): Führt einen kurzen Test mit geringer Parallelität aus, der Endpunktcaches aufwärmt und den End-to-End-Anforderungsfluss überprüft. Die Aufwärmergebnisse werden nicht für Leistungsmetriken verwendet. Im binären Suchmodus wird die Warmuplatenz auch als Basis für die Parallelitätsschätzung von Littles Gesetz verwendet.

Hauptlasttestreihe

Das Notebook führt eine Reihe von Tests mit zunehmender Client-Parallelität aus.

  • Start: Niedrige Gleichzeitigkeit (z. B. 5 gleichzeitige Clients)
  • Mittel: Mittlere Gleichzeitigkeit, z. B. 10, 20 oder 50 Clients
  • Ende: Hohe Konkurrenz (z. B. über 100 Clients)

Jeder Test wird für die Dauer ausgeführt, die in step_duration_seconds konfiguriert ist (5-10 Minuten empfohlen).

Was das Notizbuch misst

Das Notizbuch misst und meldet Folgendes:

Latenzmetriken:

  • P50 (Median): Die Hälfte der Abfragen ist schneller als dies.
  • P95: 95 % der Abfragen sind schneller als dieser Wert. Dies ist eine wichtige SLA-Metrik.
  • P99: 99% von Abfragen sind schneller als dies.
  • Max: Latenz im ungünstigsten Fall.

Durchsatzmetriken:

  • RPS (Anforderungen pro Sekunde): Erfolgreiche Abfragen pro Sekunde.
  • Gesamtabfragen: Anzahl der abgeschlossenen Abfragen.
  • Erfolgsquote: Prozentsatz der erfolgreichen Abfragen.

Fehler:

  • Abfragefehler nach Typ
  • Ausnahmemeldungen
  • Timeoutanzahl

Ergebnisspeicher

Wenn der output_table Parameter festgelegt ist, speichert das Notizbuch eine Zeile pro Parallelitätsebene (oder pro binären Suchschritt) in einer Unity Catalog Delta-Tabelle. Die Tabelle wird automatisch bei der ersten Ausführung erstellt und an nachfolgende Ausführung angefügt. Jede Zeile enthält run_name, exploration_mode, Parallelität, Erfolgs-/Fehlerraten, Latenz-Perzentile, RPS und binärsuchspezifische Felder (bs_step, bs_target_qps, bs_outcome). Auf diese Weise können Sie die Ausführung im Laufe der Zeit mithilfe von SQL- oder BI-Tools vergleichen.

Als Databricks-Auftrag ausführen

Alle Notizbuchparameter sind definiert als dbutils.widgets, die direkt den Databricks Job-Parametern zugeordnet sind. So planen oder automatisieren Sie Ladetests:

  1. Erstellen Sie einen Auftrag, bei dem das Notebook die Aufgabe ist.
  2. Legen Sie die Widgetwerte als Auftragsparameter fest. Es sind keine Codebearbeitungen erforderlich.
  3. Fügen Sie den Auftrag an einen Einzelknotencluster mit vielen CPU-Kernen an (Locust profitiert von parallelen Workern).
  4. Führen Sie bei Bedarf oder nach einem Zeitplan wiederkehrende Baseline-Tests aus.

Schritt 6. Interpretieren von Ergebnissen

Die folgende Tabelle zeigt Ziele für eine gute Leistung:

Metric Ziel Kommentar
P95-Latenz < 500 ms Die meisten Abfragen sind schnell
P99-Latenz < 1 Sekunde Angemessene Leistung bei langfristigen Abfragen
Erfolgsquote > 99.5% Niedrige Fehlerrate
Latenz im Laufe der Zeit stabil Keine Verschlechterung während des Tests festgestellt
Abfragen pro Sekunde Ziel erfüllt Endpunkt kann erwarteten Datenverkehr verarbeiten

Die folgenden Ergebnisse deuten auf eine schlechte Leistung hin:

  • P95 > 1s. Gibt an, dass Abfragen für die Echtzeitverwendung zu langsam sind.
  • P99 > 3s. Die Latenz bei Long-Tail-Abfragen beeinträchtigt die Benutzererfahrung.
  • Erfolgsquote < 99%. Zu viele Fehler.
  • Erhöhen der Latenz. Gibt die Ressourcenerschöpfung oder ein Speicherleck an.
  • Ratenbegrenzungsfehler (429). Gibt an, dass höhere Endpunktkapazität erforderlich ist.

Kompromiss zwischen RPS und Latenz

Der maximale RPS ist nicht der optimale Punkt für den Produktionsdurchsatz. Die Latenz erhöht sich nicht linear, wenn Sie den maximalen Durchsatz erreichen. Der Betrieb bei maximaler RPS führt häufig zu einer 2-5x höheren Latenz im Vergleich zum Betrieb bei 60-70% maximaler Kapazität.

Das folgende Beispiel zeigt, wie die Ergebnisse analysiert werden, um den optimalen Betriebspunkt zu finden.

  • Das maximale RPS beträgt 480 bei 150 gleichzeitigen Clients.
  • Der optimale Betriebspunkt beträgt 310 RPS bei 50 gleichzeitigen Clients (65% Kapazität).
  • Die Latenzstrafe bei max: P95 ist 4,3x höher (1,5s vs. 350 ms)
  • In diesem Beispiel empfiehlt es sich, den Endpunkt für die Kapazität von 480 RPS zu vergrößern und bei ~310 RPS zu arbeiten.
Konkurrenz P50 P95 P99 RPS Success Kapazität
5 80 ms 120 ms 150 ms 45 100 % 10 %
10 85 ms 140 ms 180 ms 88 100 % 20 %
20 95 ms 180 ms 250 ms 165 99.8% 35 %%
50 150 ms 350 ms 500 ms 310 99.2% 65% ← Optimaler Bereich
100 250 ms 800 ms 1.2s 420 97.5% 90% ⚠ Annäherung an Maximum
150 450 ms 1.5s 2.5s 480 95.0% 100% ❌ Maximale RPS

Der Betrieb mit dem maximalen RPS kann zu folgenden Problemen führen:

  • Latenzverschlechterung. Im Beispiel beträgt P95 350 ms bei 65% Kapazität, beträgt jedoch 1,5s bei 100% Kapazität.
  • Kein Platz, um Verkehrsausbrüche oder Verkehrsspitzen zu bewältigen. Bei 100% Kapazität führt jede Spitze zu einem Timeout. Mit 65%iger Kapazität kann eine 50%ige Datenverkehrsspitze ohne Probleme behandelt werden.
  • Erhöhte Fehlerraten. Im Beispiel beträgt die Erfolgsquote 99,2% bei 65% Kapazität, aber 95,0% – einer Ausfallrate von 5% – bei 100% Kapazität.
  • Risiko der Ressourcenauslastung. Bei maximaler Last nehmen Warteschlangen zu, der Arbeitsspeicherdruck steigt, Verbindungspools beginnen sich zu sättigen, und die Wiederherstellungszeit nach Vorfällen steigt.

Die folgende Tabelle zeigt empfohlene Betriebspunkte für unterschiedliche Anwendungsfälle.

Anwendungsfall Zielkapazität Grund
Latenzempfindlich (Suche, Chat) 50-60% von max. Priorisierung niedriger P95/P99-Latenzzeiten
Ausgewogene Empfehlungen 60-70% von max. Gute Balance zwischen Kosten und Latenz
Kostenoptimierte (Batchaufträge) 70-80% von max. Akzeptable höhere Latenz
Nicht empfohlen > 85% von max. Latenzspitzen, keine Burst-Kapazität

Hilfsfunktionen für die Berechnung der Betriebspunkt- und Endpunktgröße

Suchen des optimalen Punkts

Der folgende Code zeichnet QPS im Vergleich zur P95-Latenz. Suchen Sie in der Zeichnung nach dem Punkt, an dem die Kurve beginnt, stark nach oben zu biegen. Dies ist der optimale Betriebspunkt.

import matplotlib.pyplot as plt

# Plot QPS vs. P95 latency
qps_values = [45, 88, 165, 310, 420, 480]
p95_latency = [120, 140, 180, 350, 800, 1500]

plt.plot(qps_values, p95_latency, marker='o')
plt.axvline(x=310, color='green', linestyle='--', label='Optimal (65% capacity)')
plt.axvline(x=480, color='red', linestyle='--', label='Maximum (100% capacity)')
plt.xlabel('Queries Per Second (QPS)')
plt.ylabel('P95 Latency (ms)')
plt.title('QPS vs. Latency: Finding the Sweet Spot')
plt.legend()
plt.grid(True)
plt.show()

Größenempfehlungsformel

def calculate_endpoint_size(target_qps, optimal_capacity_percent=0.65):
    """
    Calculate required endpoint capacity

    Args:
        target_qps: Your expected peak production QPS
        optimal_capacity_percent: Target utilization (default 65%)

    Returns:
        Required maximum endpoint QPS
    """
    required_max_qps = target_qps / optimal_capacity_percent

    # Add 20% safety margin for unexpected bursts
    recommended_max_qps = required_max_qps * 1.2

    return {
        "target_production_qps": target_qps,
        "operate_at_capacity": f"{optimal_capacity_percent*100:.0f}%",
        "required_max_qps": required_max_qps,
        "recommended_max_qps": recommended_max_qps,
        "burst_capacity": f"{(1 - optimal_capacity_percent)*100:.0f}% headroom"
    }

# Example
result = calculate_endpoint_size(target_qps=200)
print(f"Target production QPS: {result['target_production_qps']}")
print(f"Size endpoint for: {result['recommended_max_qps']:.0f} QPS")
print(f"Operate at: {result['operate_at_capacity']}")
print(f"Available burst capacity: {result['burst_capacity']}")

# Output:
# Target production QPS: 200
# Size endpoint for: 369 QPS
# Operate at: 65%
# Available burst capacity: 35% headroom

Identifizieren Sie den Engpass im Einbettungsmodell

Wenn Ihr Index verwaltete Einbettungen verwendet, erfasst das Auslastungstest-Notizbuch die Zeitmessung pro Komponente über den debug_level=1-Parameter für jede Abfrage. Die Ergebnistabelle enthält:

  • ann_time — Zeitaufwand für die ungefähre Suche nach dem nächsten Nachbarn
  • embedding_gen_time — Zeitaufwand für die Generierung der Abfrageeinbettung auf dem Modell-Dienstendpunkt
  • reranker_time — Zeitaufwand für die Reranking (sofern aktiviert)
  • response_time — Gesamter End-to-End-Antwortzeitpunkt

Wenn embedding_gen_time im Verhältnis zu ann_time konsistent groß ist, ist der Einbettungsendpunkt der Engpass, nicht der Vektor-Suchendpunkt. Häufige Ursachen:

  • Das Einbettungsmodell, das den Endpunkt bedient, hat "Scale to zero " aktiviert. Deaktivieren Sie sie für Produktionslasttests. Siehe Vermeidung der Skalierung auf Null in der Produktion.
  • Der Einbettungsendpunkt verfügt nicht über genügend bereitgestellte Parallelität für die abfragerate, die Sie testen.
  • Der Endpunkt des Einbettungsmodells wird für andere Workloads freigegeben. Verwenden Sie einen dedizierten Endpunkt zum Ladentest.

Tipp

Um die Leistung der Vektorsuche von der Leistung des Einbettungsmodells zu isolieren, wechseln Sie zu selbstverwalteten Einbettungen für Auslastungstests . Übergeben Sie vorkompilierte Vektoren im EMBEDDING_COLUMN Parameter anstelle von Textabfragen. Dadurch wird die Einbettungslatenz vollständig aus der Messung entfernt.

Schritt 7: Größe Ihres Endpunkts

Verwenden Sie die Empfehlung des Notebooks

Nach der Analyse der Ergebnisse werden Sie vom Notizbuch aufgefordert:

  1. Wählen Sie die Zeile aus, die Ihre Latenzanforderungen am besten erfüllt.
  2. Geben Sie das gewünschte RPS Ihrer Anwendung ein.

Das Notizbuch zeigt dann eine empfohlene Endpunktgröße an. Es berechnet die erforderliche Kapazität basierend auf folgendem:

  • Ihr Ziel-RPS
  • Beobachtete Latenz auf unterschiedlichen Parallelitätsebenen
  • Erfolgsratenschwellenwerte
  • Sicherheitsmarge (in der Regel 2x erwartete Spitzenlast)

Überlegungen zur Skalierung

Standardendpunkte:

  • Automatisch skalieren, um die Indexgröße zu unterstützen
  • Manuell skalieren, um den Durchsatz zu unterstützen
  • Automatisches Herunterskalieren, wenn Indizes gelöscht werden
  • Manuelles Herunterskalieren, um die Kapazität zu reduzieren

Speicheroptimierte Endpunkte:

  • Automatisch skalieren, um die Indexgröße zu unterstützen
  • Automatisches Herunterskalieren, wenn Indizes gelöscht werden

Schritt 8: Überprüfen der endgültigen Konfiguration

Nach dem Aktualisieren der Endpunktkonfiguration:

  1. Warten Sie, bis der Endpunkt bereit ist. Dies kann mehrere Minuten dauern.
  2. Führen Sie den endgültigen Überprüfungstest im Notizbuch aus.
  3. Überprüfen Sie, ob die Leistung Ihren Anforderungen entspricht:
    • RPS ≥ Zieldurchsatz
    • P95-Latenz entspricht SLA
    • Erfolgsquote > 99,5%
    • Keine dauerhaften Fehler

Wenn die Überprüfung fehlschlägt, versuchen Sie Folgendes:

  • Erhöhen der Endpunktkapazität
  • Optimieren der Abfragekomplexität
  • Überprüfen der Filterleistung
  • Überprüfen der Konfiguration des Einbettungs-Endpunkts

Wann sollten Sie den Test erneut testen

Um die Leistungssichtbarkeit aufrechtzuerhalten, ist es ratsam, vierteljährlich Baseline-Auslastungstests durchzuführen. Sie sollten auch erneut testen, wenn Sie eine der folgenden Änderungen vornehmen:

  • Ändern von Abfragemustern oder Komplexität
  • Aktualisieren des Vektorsuchindex
  • Ändern von Filterkonfigurationen
  • Erhebliche Verkehrsaufkommen erwarten
  • Bereitstellen neuer Features oder Optimierungen
  • Wechseln von Standard zu speicheroptimierten Endpunkttypen

Problembehandlung

Alle Anforderungen schlagen mit ~10 ms Latenz und 240-Byteantworten fehl.

Dies gibt an, dass der Dienstprinzipal eine Antwort vom Typ 401/403 empfängt. Überprüfen:

  1. Der Dienstprinzipal verfügt über Can Query-Berechtigungen für den Vektorsuchendpunkt (nicht nur den Index).
  2. Der geheime Bereich enthält gültige service_principal_client_id Schlüssel und service_principal_client_secret Schlüssel.
  3. Der OAuth-Geheimschlüssel ist nicht abgelaufen.

Das Notizbuch enthält eine Konnektivitätsprüfung, die dies abfangen kann, bevor der vollständige Ladetest ausgeführt wird.

Ausführen mehrerer Auslastungstestaufträge auf demselben Cluster

Wenn Sie zwei Lasttestaufträge gleichzeitig auf demselben Cluster ausführen, kann es passieren, dass ein Auftrag veraltete OAuth-Token erhält oder es zu einer CPU-Ressourcenkonkurrenz mit den Locust-Arbeitsprozessen des anderen Auftrags kommt. Um zuverlässige Ergebnisse zu erzielen, führen Sie Ladetestaufträge einzeln auf einem dedizierten Cluster aus.

Komponentenzeitdiagramme sind leer

Die Zeitdiagramme der Komponente (ann_time, embedding_gen_time, reranker_time) erfordern, dass der Endpunkt debug_info in Abfrageantworten zurückliefert. Wenn diese Diagramme leer sind:

  • Vergewissern Sie sich, dass Sie das fast_vs_load_test_async_load.py-Skript verwenden (das debug_info aus Antworten parst) als locust_script_path.
  • Einige Endpunktkonfigurationen geben möglicherweise nicht zurück debug_info. Selbstverwaltete Einbettungsindizes geben in der Regel zurück ann_time , response_time aber nicht embedding_gen_time oder reranker_time.

Ergebnistabelle, die nicht aus einem SQL-Lagerhaus abfragt werden kann

Das Notizbuch schreibt Ergebnisse aus der Spark-Sitzung des Clusters. Wenn in einem SQL Warehouse 0 Zeilen für eine Tabelle angezeigt werden, die das Notizbuch als ausgefüllt meldet, kann es sich bei dem Problem um eine Synchronisierungsverzögerung der Unity-Katalogmetadaten handeln. Warten Sie einige Minuten, und versuchen Sie es erneut, oder fragen Sie die Tabelle direkt von einem Notizbuch ab, das an denselben Cluster angefügt ist.

Zusammenfassung der bewährten Methoden

Testkonfiguration

  • Führen Sie Tests für mindestens 5 Minuten bei Spitzenlast aus.

  • Verwenden Sie OAuth-Dienstprinzipale für die Authentifizierung.

  • Erstellen Sie realistische Abfragenutzlasten, die den erwarteten Produktionsabfragen entsprechen.

  • Testen Sie mit produktionsähnlichen Filtern und Parametern.

  • Schließen Sie vor der Messung eine Warmphase ein.

  • Testen Sie auf mehreren Parallelitätsebenen.

  • Verfolgen Sie P95/P99 Latenzen, nicht nur Mittelwerte.

  • Testen Sie sowohl zwischengespeicherte als auch nicht zwischengespeicherte Leistung.

    # Conservative approach: Size endpoint for UNCACHED performance
    uncached_results = run_load_test(diverse_queries, duration=600)
    endpoint_size = calculate_capacity(uncached_results, target_rps=500)
    
    # Then verify cached performance is even better
    cached_results = run_load_test(repetitive_queries, duration=300)
    print(f"Cached P95: {cached_results['p95']}ms (bonus performance)")
    

Abfragesatzentwurf

  • Passen Sie Ihre Testabfragevielfalt an die tatsächliche Datenverkehrsverteilung (häufige und seltene Abfragen) an.
  • Verwenden Sie tatsächliche Abfragen aus Protokollen (anonymisiert).
  • Fügen Sie verschiedene Abfragekomplexitäten ein.
  • Testen Sie sowohl zwischengespeicherte als auch nicht zwischengespeicherte Szenarien, und verfolgen Sie die Ergebnisse separat.
  • Testen Sie mit erwarteten Filterkombinationen.
  • Verwenden Sie dieselben Parameter, die Sie in der Produktion verwenden werden. Wenn Sie beispielsweise die Hybridsuche in der Produktion verwenden, schließen Sie Hybridsuchabfragen ein. Verwenden Sie einen ähnlichen num_results Parameter wie in der Produktion.
  • Verwenden Sie keine Abfragen, die nie in der Produktion auftreten.

Leistungsoptimierung

Wenn Latenzen zu hoch sind, versuchen Sie Folgendes:

  1. Verwenden Sie OAuth-Dienstprinzipien (nicht PATs) – Verbesserung um 100 ms
  2. Reduzieren num_results – Das Abrufen von 100 Ergebnissen ist langsamer als 10
  3. Optimieren von Filtern – Komplexe oder übermäßig restriktive Filter verlangsamen Abfragen
  4. Überprüfen des Einbettungsendpunkts – Stellen Sie sicher, dass er nicht auf Null skaliert wird oder über genügend Bandbreite verfügt.

Wenn Sie die Geschwindigkeitsbeschränkungen erreichen, probieren Sie Folgendes aus:

  1. Erhöhen der Endpunktkapazität – Skalieren Ihres Endpunkts
  2. Implementierung von clientseitiger Ratenbegrenzung oder zeitlich verteilten Abfragen
  3. Verwenden von Verbindungspooling – Wiederverwenden von Verbindungen
  4. Wiederholungslogik hinzufügen – Exponentielles Backoff verwenden (bereits Teil des Python SDK)

Weitere Ressourcen