Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
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.jsonDatei, 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 denlocust_script_pathNotizbuchparameter 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
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
Erstellen Sie einen Databricks-Dienstprinzipal. Anweisungen finden Sie unter Hinzufügen von Dienstprinzipalen zu Ihrem Konto.
Berechtigungen erteilen:
- Navigieren Sie zur Vektor-Suchendpunkt-Seite.
- Klicken Sie auf Berechtigungen.
- Erteilen Sie dem Dienstprinzipal Can Query-Berechtigungen.
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.
Speichern Sie Anmeldeinformationen sicher.
- Erstellen Sie einen Databricks-Geheimhaltungsbereich. Anweisungen finden Sie im Lernprogramm: Erstellen und Verwenden eines geheimen Databricks-Schlüssels.
- Wie im folgenden Codebeispiel gezeigt, speichern Sie die Dienstprinzipal-Client-ID als
service_principal_client_idund speichern Sie den OAuth-Geheimschlüssel alsservice_principal_client_secret.
# 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:
- Beginnen Sie bei
max_target_qps / 2(250 im Beispiel). - 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.). - 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.
- Wiederholen Sie diesen Vorgang bis zu
exploration_stepsSchritte (Standard 8) oder bis der Suchbereich auf 5 % vonmax_target_qpseingegrenzt 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 |
|
Ein Produktempfehlungs-Widget, das "trending items" anzeigt – die gleiche Abfrage wird tausende mal pro Stunde ausgeführt. |
| Verschiedene Abfragen |
|
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:
- Navigieren Sie im Arbeitsbereich "Databricks" zu Ihrem Vektorsuchendpunkt.
- Klicken Sie in der linken Randleiste auf Dienste.
- Wählen Sie Ihren Endpunkt aus.
- Klicken Sie auf Verwenden → Abfrage.
- Fügen Sie Ihren
input.jsonInhalt in das Abfragefeld ein. - Ü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:
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
PermissionErrorfehl, statt einen vollständigen Belastungstest auszuführen, der nur Fehlerdaten erzeugt. Dies spart Zeit, wenn Anmeldeinformationen oder Berechtigungen falsch konfiguriert sind.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:
- Erstellen Sie einen Auftrag, bei dem das Notebook die Aufgabe ist.
- Legen Sie die Widgetwerte als Auftragsparameter fest. Es sind keine Codebearbeitungen erforderlich.
- Fügen Sie den Auftrag an einen Einzelknotencluster mit vielen CPU-Kernen an (Locust profitiert von parallelen Workern).
- 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:
- Wählen Sie die Zeile aus, die Ihre Latenzanforderungen am besten erfüllt.
- 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:
- Warten Sie, bis der Endpunkt bereit ist. Dies kann mehrere Minuten dauern.
- Führen Sie den endgültigen Überprüfungstest im Notizbuch aus.
- Ü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:
- Der Dienstprinzipal verfügt über Can Query-Berechtigungen für den Vektorsuchendpunkt (nicht nur den Index).
- Der geheime Bereich enthält gültige
service_principal_client_idSchlüssel undservice_principal_client_secretSchlüssel. - 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 (dasdebug_infoaus Antworten parst) alslocust_script_path. - Einige Endpunktkonfigurationen geben möglicherweise nicht zurück
debug_info. Selbstverwaltete Einbettungsindizes geben in der Regel zurückann_time,response_timeaber nichtembedding_gen_timeoderreranker_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_resultsParameter wie in der Produktion. - Verwenden Sie keine Abfragen, die nie in der Produktion auftreten.
Leistungsoptimierung
Wenn Latenzen zu hoch sind, versuchen Sie Folgendes:
- Verwenden Sie OAuth-Dienstprinzipien (nicht PATs) – Verbesserung um 100 ms
- Reduzieren
num_results– Das Abrufen von 100 Ergebnissen ist langsamer als 10 - Optimieren von Filtern – Komplexe oder übermäßig restriktive Filter verlangsamen Abfragen
- Ü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:
- Erhöhen der Endpunktkapazität – Skalieren Ihres Endpunkts
- Implementierung von clientseitiger Ratenbegrenzung oder zeitlich verteilten Abfragen
- Verwenden von Verbindungspooling – Wiederverwenden von Verbindungen
- Wiederholungslogik hinzufügen – Exponentielles Backoff verwenden (bereits Teil des Python SDK)