Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questa pagina fornisce indicazioni, codice di esempio e un notebook di esempio per il test di carico degli endpoint di ricerca vettoriale. I test di carico consentono di comprendere le prestazioni e l'idoneità di produzione di un endpoint di ricerca vettoriale prima della distribuzione nell'ambiente di produzione. I test di carico possono indicare:
- Latenza a livelli di scalabilità diversi
- Limiti di velocità effettiva e colli di bottiglia (richieste al secondo, suddivisione della latenza)
- Tassi di errore sotto carico sostenuto
- Utilizzo delle risorse e pianificazione della capacità
Per altre informazioni sui test di carico e sui concetti correlati, vedere Test di carico per la gestione degli endpoint.
Requisiti
Prima di iniziare questi passaggi, è necessario disporre di un endpoint di ricerca vettoriale distribuito e di un'entità servizio con autorizzazioni Can Query per l'endpoint. Vedere Passaggio 1: Configurare l'autenticazione dell'entità servizio.
Scaricare e importare una copia dei file seguenti e del notebook di esempio nell'area di lavoro di Azure Databricks:
-
input.json. Questo è un esempio del
input.jsonfile che specifica il payload inviato da tutte le connessioni simultanee all'endpoint. Se necessario, è possibile avere più file. Se si usa il notebook di esempio, questo file viene generato automaticamente dalla tabella di input fornita. - fast_vs_load_test_async_load.py. Questo script viene usato dal notebook di esempio per l'autenticazione e la gestione del payload.
- Il notebook di esempio seguente, che esegue i test di carico. Per ottenere prestazioni ottimali, eseguire questo notebook in un cluster con un numero elevato di core e memoria elevata. La memoria è necessaria per le query con incorporamenti pregenerati, poiché gli incorporamenti sono spesso a elevato utilizzo di memoria.
Notebook di esempio e introduzione rapida
Usare il notebook di esempio seguente per iniziare. Include tutti i passaggi per eseguire un test di carico. È necessario immettere alcuni parametri, ad esempio i segreti di Databricks, il nome dell'endpoint e così via.
Notebook di test di carico di Locust
Framework di test di carico: Locust
Locust è un framework di test del carico open source che consente di eseguire le operazioni seguenti:
- Variare il numero di connessioni client simultanee
- Controllare la velocità con cui le connessioni si instaurano.
- Misurare le prestazioni degli endpoint durante il test
- Rilevamento automatico e uso di tutti i core CPU disponibili
Il notebook di esempio usa il --processes -1 flag per rilevare automaticamente i core CPU e usarli completamente.
Se il Locust è limitato dalla CPU, viene visualizzato un messaggio nell'output.
Passaggio 1: Configurare l'autenticazione del principale del servizio
Importante
Per i test delle prestazioni simili all'ambiente di produzione, usare sempre l'autenticazione del principale di servizio OAuth. Le entità servizio offrono fino a 100 ms di tempo di risposta più veloce e limiti di frequenza delle richieste più elevati rispetto ai token di accesso personale .
Creare e configurare un'entità servizio
Creare un'entità servizio Databricks. Per istruzioni, vedere Aggiungere entità servizio all'account.
Concedere le autorizzazioni:
- Passare alla pagina dell'endpoint di ricerca vettoriale.
- Fare clic su Autorizzazioni.
- Assegnare all'entità del servizio le autorizzazioni Can Query.
Creare un segreto OAuth.
- Passare alla pagina dei dettagli dell'entità servizio.
- Fare clic sulla scheda Segreti .
- Fare clic su Genera segreto.
- Impostare la durata (consigliare 365 giorni per i test a lungo termine).
- Copiare immediatamente sia l'ID client che il segreto.
Archiviare le credenziali in modo sicuro.
- Creare un ambito segreto di Databricks. Per le istruzioni, vedere Esercitazione: Creare e usare un segreto Databricks.
- Come illustrato nell'esempio di codice seguente, archiviare l'ID client dell'entità servizio come
service_principal_client_ide archiviare il segreto OAuth comeservice_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>")
Passaggio 2: Configurare il test di carico
Configurazione del notebook
Nella copia del notebook di esempio configurare questi parametri:
| Parametro | Description | Valore consigliato |
|---|---|---|
endpoint_name |
Nome dell'endpoint di ricerca vettoriale | Nome dell'endpoint |
index_name |
Nome dell'indice completo (catalog.schema.index) |
Nome dell'indice |
locust_run_time |
Durata per ogni singolo test di carico | 300-600 secondi (5-10 minuti) |
csv_output_prefix |
Prefisso per i file di output CSV | load_test_ |
secret_scope_name |
Nome dell'ambito segreto di Databricks | Nome dell'ambito |
Perché 5-10 minuti?
Una durata minima del test di 5 minuti è fondamentale.
- Le query iniziali possono includere un sovraccarico di avvio a freddo.
- Gli endpoint richiedono tempo per raggiungere le prestazioni dello stato stabile.
- Il ridimensionamento automatico del modello che gestisce gli endpoint (se abilitato) richiede tempo per l'attivazione.
- I test di breve durata non rilevano i comportamenti di riduzione delle prestazioni sotto carico sostenuto.
La tabella seguente illustra la durata consigliata dei test a seconda dell'obiettivo del test.
| Tipo di test | Durata del test | Obiettivi del test |
|---|---|---|
| Test di fumo rapido | 2-3 minuti | Verificare le funzionalità di base |
| Linea di base delle prestazioni | 5-10 minuti | Metriche di stato stabile affidabili |
| Test da sforzo | 15-30 minuti | Identificare l'esaurimento delle risorse |
| Test di resistenza | 1-4 ore | Riduzione delle prestazioni, stabilità della latenza |
Passaggio 3. Progettare il set di query
Quando possibile, il set di query deve riflettere il traffico di produzione previsto il più possibile. In particolare, è consigliabile provare a corrispondere alla distribuzione prevista delle query in termini di contenuto, complessità e diversità.
Usare query realistiche. Non usare testo casuale, ad esempio "test query 1234".
Corrisponde alla distribuzione prevista del traffico di produzione. Se si prevedono 80% query comuni, 15% query a media frequenza e 5% query non frequenti, il set di query deve riflettere tale distribuzione.
Abbina il tipo di query che ti aspetti di vedere in produzione. Ad esempio, se si prevede che le query di produzione usino filtri oricerche ibride, è consigliabile usarle anche nel set di query.
Query di esempio con filtri:
{ "query_text": "wireless headphones", "num_results": 10, "filters": { "brand": "Sony", "noise_canceling": true } }Query di esempio con la ricerca ibrida:
{ "query_text": "best noise canceling headphones for travel", "query_type": "hybrid", "num_results": 10 }
Diversità delle query e memorizzazione nella cache
Gli endpoint di ricerca vettoriale memorizzano nella cache diversi tipi di risultati delle query per migliorare le prestazioni. Questa memorizzazione nella cache può influire sui risultati dei test di carico. Per questo motivo, è importante prestare attenzione alla diversità del set di query. Ad esempio, se si invia ripetutamente lo stesso set di query, si sta testando la cache, non le prestazioni effettive della ricerca.
| Utilizzo: | Quando: | Example |
|---|---|---|
| Query identiche o poche |
|
Widget di raccomandazione del prodotto che mostra "trend": la stessa query viene eseguita migliaia di volte all'ora. |
| Varietà di query |
|
Una ricerca di e-commerce in cui ogni utente digita ricerche di prodotti diversi. |
Per altre raccomandazioni, vedere Riepilogo delle procedure consigliate.
Opzioni per la creazione di un set di query
Le schede del codice mostrano tre opzioni per la creazione di un set di query diversificato. Non c'è nessuna dimensione adatta a tutti. Scegli quello che funziona meglio per te.
- (Scelta consigliata) Campionamento casuale dalla tabella di input dell'indice. Questo è un buon punto di partenza generale.
- Campionamento dai registri di produzione. Questo è un buon punto di partenza se sono presenti log di produzione. Tenere presente che le query cambiano in genere nel tempo, quindi aggiornare regolarmente il set di test per mantenerlo aggiornato.
- Generazione di query sintetiche. Ciò è utile se non si dispone di log di produzione o se si usano filtri complessi.
Campionamento casuale dalla tabella dei dati di input
Nell'esempio di codice seguente viene eseguita una query casuale dalla tabella di input dell'indice.
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")
Esempio dai log di produzione
Gli esempi di codice seguenti provengono proporzionalmente dalle query di produzione.
# 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)
Query sintetiche
Se non sono ancora disponibili log di produzione, è possibile generare query sintetiche diverse.
# 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")
Passaggio 4. Verifica il payload
Prima di eseguire il test di carico completo, convalidare il payload:
- Nell'area di lavoro Databricks passare all'endpoint di ricerca vettoriale.
- Nella barra laterale sinistra fare clic su Serve.
- Seleziona il tuo endpoint.
- Fare clic su Usa → query.
- Incollare il
input.jsoncontenuto nella casella di query. - Verificare che l'endpoint restituisca i risultati previsti.
In questo modo, il test di carico misurerà query realistiche, anziché risposte di errore.
Passaggio 5. Eseguire il test di carico
Test di riscaldamento iniziale (30 secondi)
Il notebook esegue prima un test di 30 secondi che esegue le operazioni seguenti:
- Conferma che l'endpoint è online e risponde
- Riscalda tutte le cache
- Convalida l'autenticazione
I risultati di questo test di riscaldamento includono un sovraccarico di avvio a freddo, quindi non deve essere usato per le metriche delle prestazioni.
Serie principale di test di carico
Il notebook esegue una serie di test con una concorrenza client crescente:
- Inizio: concorrenza bassa (ad esempio, 5 client simultanei)
- Centrale: concorrenza media (ad esempio, 10, 20 o 50 client)
- Fine: concorrenza elevata (ad esempio, oltre 100 client)
Ogni test viene eseguito per il tempo configurato locust_run_time (consigliato tra 5 e 10 minuti).
Cosa misura il notebook
Il notebook misura e segnala quanto segue:
Metriche di latenza:
- P50 (median): La metà delle query è più veloce di questa.
- P95: Il 95% delle query sono più veloci di questa. Si tratta di una metrica chiave del contratto di servizio.
- P99: Il 99% delle query sono più veloci di queste.
- Massimo: Latenza peggiore.
Metriche della velocità effettiva:
- RPS (richieste al secondo): Interrogazioni riuscite al secondo.
- Totale delle query: Numero di query completate.
- Tasso di successo: Percentuale di query riuscite.
Errori:
- Fallimenti delle query per tipo
- Messaggi di eccezione
- Conteggi di timeout
Passaggio 6. Interpretare i risultati
La tabella seguente mostra gli obiettivi di buone prestazioni:
| Metrica | Obiettivo | Comment |
|---|---|---|
| Latenza P95 | < 500 ms | La maggior parte delle query sono veloci |
| Latenza P99 | < 1s | Prestazioni ragionevoli nelle query a lunga coda |
| Tasso di successo | > 99,5% | Bassa frequenza di errori |
| Latenza nel tempo | Stabile | Nessuna riduzione osservata durante il test |
| Query al secondo | Soddisfa la destinazione | L'endpoint può gestire il traffico previsto |
I risultati seguenti indicano prestazioni scarse:
- P95 > 1s. Indica che le query sono troppo lente per l'uso in tempo reale.
- P99 > 3s. La latenza nelle query a lunga coda danneggerà l'esperienza dell'utente.
- Percentuale di successo < 99%. Troppi errori.
- Aumento della latenza. Indica l'esaurimento delle risorse o la perdita di memoria.
- Errori di limitazione della frequenza (429). Indica che è necessaria una capacità endpoint superiore.
Compromesso tra RPS e latenza
Il valore rps massimo non è il punto ottimale per la velocità effettiva di produzione. La latenza aumenta in modo non lineare man mano che si avvicina la velocità effettiva massima. L'utilizzo di RPS massimo comporta spesso una latenza 2-5 volte superiore rispetto al funzionamento a 60-70% della capacità massima.
Nell'esempio seguente viene illustrato come analizzare i risultati per trovare il punto operativo ottimale.
- Il numero massimo di RPS è 480 a 150 client simultanei.
- Il punto operativo ottimale è 310 RPS a 50 client simultanei (65% capacità).
- La penalità di latenza massima: P95 è 4,3 volte superiore (1,5 e 350 ms)
- In questo esempio, è consigliabile ridimensionare l'endpoint per la capacità di 480 RPS e operare a circa 310 RPS.
| Concurrency | P50 | P95 | P99 | RPS | Success | Capacity |
|---|---|---|---|---|---|---|
| 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% ← Punto ideale |
| 100 | 250 ms | 800 ms | 1.2s | 420 | 97,5% | 90% ⚠️ Vicino al massimo |
| 150 | 450 ms | 1,5 | 2.5s | 480 | 95.0% | 100% ❌ RPS massimo |
L'uso al massimo di RPS può causare i problemi seguenti:
- Riduzione della latenza. Nell'esempio, P95 è di 350 ms al 65% della capacità, ma è di 1,5 s al 100% della capacità.
- Nessun margine per gestire incrementi o picchi di traffico. A 100% capacità, qualsiasi picco causa un timeout. A 65% capacità, un picco di traffico di 50% può essere gestito senza problemi.
- Aumento delle percentuali di errore. Nell'esempio, il tasso di successo è 99,2% a 65% capacità, ma 95,0%, ovvero una percentuale di errore di 5%, a 100% capacità.
- Rischio di esaurimento delle risorse. Con un carico massimo, le code aumentano, aumentano la pressione della memoria, i pool di connessioni iniziano a saturazione e il tempo di ripristino dopo gli eventi imprevisti aumenta.
La tabella seguente illustra i punti operativi consigliati per diversi casi d'uso.
| Caso d'uso | Capacità di destinazione | Motivazione |
|---|---|---|
| Sensibile alla latenza (ricerca, chat) | 50-60% del massimo | Classificare in ordine di priorità la latenza P95/P99 bassa |
| Bilanciato (raccomandazioni) | 60-70% del massimo | Buon equilibrio tra costi e latenza |
| Ottimizzazione dei costi (lavori batch) | 70-80% del massimo | Latenza più elevata accettabile |
| Non consigliato | > 85% del massimo | Picchi di latenza, nessuna capacità burst |
Funzioni helper per il calcolo delle dimensioni del punto operativo e dell'endpoint
Trovare il punto ottimale
Il codice seguente traccia QPS rispetto alla latenza P95. Nel tracciato cercare il punto in cui la curva inizia a piegarsi bruscamente verso l'alto. Si tratta del punto operativo ottimale.
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()
Formula di raccomandazione delle dimensioni
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
Passaggio 7: Dimensionare l'endpoint
Utilizzare la raccomandazione del notebook
Dopo aver analizzato i risultati, il notebook chiede di:
- Selezionare la riga più adatta ai requisiti di latenza.
- Immettere il rps desiderato dell'applicazione.
Il notebook visualizza quindi una dimensione dell'endpoint consigliata. Calcola la capacità necessaria in base ai seguenti elementi:
- RPS di destinazione
- Latenza osservata a livelli di concorrenza diversi
- Soglie della frequenza di riuscita
- Margine di sicurezza (in genere 2 volte carico massimo previsto)
Considerazioni sulla scalabilità
Endpoint standard:
- Aumentare automaticamente le prestazioni per supportare le dimensioni dell'indice
- Aumentare manualmente la capacità per supportare il throughput
- Ridurre automaticamente le prestazioni quando vengono eliminati gli indici
- Ridurre manualmente per diminuire la capacità
Endpoint ottimizzati per l'archiviazione:
- Aumentare automaticamente le prestazioni per supportare le dimensioni dell'indice
- Ridurre automaticamente le prestazioni quando vengono eliminati gli indici
Passaggio 8: Convalidare la configurazione finale
Dopo aver aggiornato la configurazione dell'endpoint:
- Attendere che l'endpoint sia pronto. Ciò può richiedere alcuni minuti.
- Eseguire il test di convalida finale nel notebook.
- Verificare che le prestazioni soddisfino i requisiti:
- RPS ≥ velocità effettiva di destinazione
- La latenza P95 soddisfa il contratto di servizio
- Percentuale di successo > 99,5%
- Nessun errore sostenuto
Se la convalida non riesce, provare a eseguire le operazioni seguenti:
- Aumentare la capacità degli endpoint
- Ottimizzare la complessità delle query
- Esaminare le prestazioni del filtro
- Controllare la configurazione dell'endpoint di incorporamento
Quando ripetere il test
Per mantenere la visibilità delle prestazioni, è consigliabile eseguire i test di carico di base trimestralmente. È anche consigliabile ripetere il test quando si apporta una delle modifiche seguenti:
- Modificare i modelli di query o la complessità della query
- Aggiornare l'indice di ricerca vettoriale
- Modificare le configurazioni dei filtri
- Aspettatevi un aumento significativo del traffico
- Distribuire nuove funzionalità o ottimizzazioni
- Passare dai tipi di endpoint standard ai tipi di endpoint ottimizzati per l'archiviazione
Riepilogo delle procedure consigliate
Configurazione di test
Eseguire test per almeno 5 minuti al carico massimo.
Utilizzare i principali di servizio OAuth per l'autenticazione.
Creare carichi di query realistici che corrispondano alle query di produzione previste.
Eseguire il test con filtri e parametri simili alla produzione.
Includere un periodo di riscaldamento prima di misurare.
Testare a più livelli di concorrenza.
Tenere traccia delle latenze P95/P99, non solo medie.
Testare sia le prestazioni memorizzate nella cache che le prestazioni non memorizzate nella cache.
# 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)")
Progettazione del set di query
- Associare la diversità delle query di test alla distribuzione del traffico reale (query frequenti e rare).
- Usare le query reali dai log (anonimizzati).
- Includere complessità diverse nelle query.
- Testare gli scenari memorizzati nella cache e non memorizzati nella cache e tenere traccia dei risultati separatamente.
- Testare con le combinazioni di filtri previste.
- Usare gli stessi parametri che verranno usati nell'ambiente di produzione. Ad esempio, se utilizzi la ricerca ibrida in produzione, includi le query relative alla ricerca ibrida. Usare un parametro simile
num_resultsa quello nell'ambiente di produzione. - Non usare query mai eseguite nell'ambiente di produzione.
Ottimizzazione delle prestazioni
Se le latenze sono troppo elevate, provare a eseguire le operazioni seguenti:
- Usare i principali del servizio OAuth (non PAT) - Miglioramento di 100 ms
- Ridurre
num_results- Recuperare 100 risultati è più lento rispetto a 10 - Ottimizzare i filtri - Filtri complessi o eccessivamente restrittivi rallentano le query
- Controllare l'endpoint di incorporamento: assicurarsi che non sia ridimensionato su zero o abbia una larghezza di banda sufficiente
Se si stanno raggiungendo i limiti di velocità, provare quanto segue:
- Aumentare la capacità degli endpoint: scalare il tuo endpoint
- Implementare la limitazione della frequenza lato client oppure distribuire le query nel tempo.
- Usare il pool di connessioni - Riutilizzare le connessioni
- Aggiungere la logica di ripetizione dei tentativi: usare il backoff esponenziale (già parte dello SDK di Python)