Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Esta página fornece orientações, código de exemplo e um caderno de exemplo para testes de carga em endpoints de pesquisa vetorial. Os testes de carga ajudam-no a compreender o desempenho e a prontidão para produção de um endpoint de pesquisa vetorial antes de ser implementado para produção. Os testes de carga podem informar-te sobre:
- Latência em diferentes níveis de escalonamento
- Limites de rendimento e gargalos (pedidos por segundo, decomposição da latência)
- Taxas de erro sob carga sustentada
- Utilização de recursos e planeamento de capacidade
Para obter mais informações sobre testes de carga e conceitos relacionados, consulte Teste de carga para pontos de serviço de extremidade.
Requerimentos
Antes de iniciar estes passos, deve ter um endpoint de pesquisa vetorial implementado e um principal de serviço com permissões Can Query no endpoint. Veja o Passo 1: Configurar a autenticação do principal de serviço.
Transfira e importe uma cópia dos seguintes ficheiros e bloco de notas de exemplo para a sua área de trabalho do Azure Databricks:
-
input.json. Este é um exemplo do
input.jsonficheiro que especifica a carga útil enviada por todas as ligações concorrentes para o seu endpoint. Podes ter vários ficheiros se necessário. Se usar o caderno de exemplo, este ficheiro é gerado automaticamente a partir da tabela de entrada fornecida. -
fast_vs_load_test_async_load.py. Carrega este script para o teu espaço de trabalho (por exemplo,
/Workspace/Users/<your-username>/fast_vs_load_test_async_load.py) e define olocust_script_pathparâmetro do caderno para o seu caminho. Este script trata da autenticação, entrega da carga útil e recolha de métricas de depuração. - O seguinte caderno de exemplo, que executa os testes de carga. Para melhor desempenho, execute este notebook num cluster de nó único com um grande número de núcleos (o Locust escala em todos os núcleos de CPU disponíveis). Recomenda-se memória elevada para consultas com embeddings pré-gerados.
Exemplo de caderno e início rápido
Use o seguinte caderno de exemplo para começar. Suporta dois modos de exploração: uma varredura gradual que testa os níveis específicos de concorrência que define, e um modo de pesquisa binária que encontra automaticamente o QPS (ponto de rutura) máximo sustentável em poucos passos. Todos os parâmetros são configurados usando widgets, para que o caderno possa correr de forma interativa ou como um Trabalho Databricks sem edições de código.
Caderno de teste de carga Locust
Framework de testes de carga: Locust
O Locust é um framework de testes de carga open-source que permite fazer o seguinte:
- Varie o número de ligações simultâneas ao cliente
- Controle a velocidade com que as conexões são iniciadas
- Mede o desempenho dos endpoints ao longo do teste
- Deteção automática e utilização de todos os núcleos de CPU disponíveis
O portátil de exemplo usa a --processes -1 bandeira para detetar automaticamente os núcleos da CPU e utilizá-los ao máximo.
Se o Locust estiver limitado pelo CPU, aparece uma mensagem na saída.
Passo 1: Configurar a autenticação do principal de serviço
Importante
Para testes de desempenho com características semelhantes à produção, utilize sempre a autenticação do principal de serviço OAuth. As entidades de serviço fornecem um tempo de resposta até 100ms mais rápido e limites de taxa de solicitação mais elevados em comparação com os tokens de acesso pessoal (PATs).
Criar e configurar o principal de serviço
Crie um principal de serviço do Databricks. Para instruções, consulte Adicionar princípios de serviço à sua conta.
Conceder permissões:
- Navegue até à sua página do ponto final da pesquisa vetorial.
- Clique em Permissões.
- Conceda permissões Pode Consultar ao principal do serviço.
Cria um segredo OAuth.
- Vá à página de detalhes do principal de serviço.
- Clique no separador Segredos.
- Clique em Gerar segredo.
- Defini a vida útil (recomendo 365 dias para testes a longo prazo).
- Copie imediatamente tanto o ID do Cliente como o Secreto.
Armazene as credenciais de forma segura.
- Crie um âmbito de segredo no Databricks. Para instruções, veja Tutorial: Criar e usar um segredo Databricks.
- Como mostrado no seguinte exemplo de código, armazene o ID do cliente principal de serviço como
service_principal_client_ide armazene o segredo OAuth comoservice_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>")
Passo 2: Configure o seu teste de carga
Configuração do portátil
Configura os parâmetros do caderno usando os widgets no topo do caderno. Ao executar o notebook como um Databricks Job, passe estes valores como parâmetros do Job. Não são necessárias edições de código.
| Parâmetro | Description | Valor Recomendado |
|---|---|---|
endpoint_name |
Nome do seu endpoint de pesquisa vetorial | O nome do seu endpoint |
index_name |
Nome completo do índice (catalog.schema.index) |
nome do índice |
test_table |
Tabela de origem para exemplos de consultas a partir de (catalog.schema.table) |
A sua tabela de entrada de índice |
query_column |
Coluna de texto a usar para incorporações geridas. | Deixe como text ou defina como o nome da sua coluna |
embedding_column |
Coluna contendo vetores de incorporação pré-calculados. Apenas utilizado para embeddings geridos por si próprio. | Deixe em branco para embeddings gerenciados. |
sample_size |
Número de consultas a amostrar para o teste | 1000 |
target_concurrencies |
Lista separada por vírgulas de contagens de clientes concorrentes a testar | 5,10,20,50 |
step_duration_seconds |
Duração em segundos por nível de concorrência. Um valor aplica-se a todos os níveis, ou fornece um por nível como uma lista separada por vírgulas. |
300 (5 minutos) |
secret_scope_name |
Nome do seu escopo secreto Databricks | Nome do seu âmbito |
locust_script_path |
Caminho do espaço de trabalho para o fast_vs_load_test_async_load.py script |
/Workspace/Users/<your-username>/fast_vs_load_test_async_load.py |
output_table |
(Opcional) Tabela Delta para armazenar resultados em (catalog.schema.table). Criado automaticamente na primeira execução. |
catalog.schema.load_test_results |
run_name |
Nomeie ou comente para marcar esta execução para análise posterior | Um rótulo descritivo |
exploration_mode |
gradual percorre target_concurrencies por ordem.
binary_search encontra automaticamente o ponto de rutura (ver Exploração do ponto de rutura). |
gradual |
max_target_qps |
(binary_search apenas) Limite superior para a pesquisa QPS |
500 |
exploration_steps |
(binary_search apenas) Número máximo de iterações de pesquisa binária |
8 |
error_rate_threshold |
(binary_search apenas) Taxa máxima aceitável de erro (%) para que um passo seja contado como sucesso |
1.0 |
num_results |
Número de resultados a devolver por consulta | 10 |
columns_to_return |
Lista separada por vírgulas de colunas a devolver nos resultados da consulta (por exemplo, id,text). Deixe em branco para retornar todas as colunas. |
Deixe em branco para padrão |
Embeddings geridos vs. autogeridos
O caderno suporta tanto embeddings geridos (onde o Databricks gera embeddings em tempo de consulta) como embeddings autogeridos (onde se passam diretamente vetores pré-computados). Configure os parâmetros apropriados com base no seu tipo de índice.
| Tipo de índice | Parâmetro a definir | Deixar indefinido |
|---|---|---|
| Incoporações geridas (modelo de incorporação gerido por Databricks no índice Delta Sync) |
query_column — o nome da coluna de texto a usar como consulta |
embedding_column (deixar em branco) |
| Embeddings autogeridos (Delta Sync ou índice de Acesso Vetorial Direto com vetores pré-computados) |
embedding_column — a coluna que contém vetores de embedding pré-computados |
query_column |
Observação
Para índices de indexação geridos, o teste de carga mede a latência fim-a-fim, incluindo o tempo de geração de indexação. Se o seu ponto final de incorporação escalar para zero, ocorrerá uma sobrecarga inicial na primeira execução do teste. Veja Identifique o gargalo do modelo de embedding para saber como isolar a latência de embedding da latência de pesquisa.
Porquê 5-10 minutos?
Uma duração mínima de 5 minutos é fundamental.
- As consultas iniciais podem incluir custos de arranque a frio.
- Os endpoints precisam de tempo para atingir desempenho em estado estável.
- A escalabilidade automática do modelo que serve os endpoints (se ativada) demora algum tempo a ativar.
- Testes curtos não detectam comportamentos de limitação sob carga sustentada.
A tabela seguinte mostra as durações recomendadas do teste consoante o seu objetivo.
| Tipo de ensaio | Duração do teste | Objetivos do teste |
|---|---|---|
| Teste rápido de fumo | 2-3 minutos | Verificar a funcionalidade básica |
| Linha de base de desempenho | 5-10 minutos | Métricas fiáveis de regime estacionário |
| Testes de esforço | 15 a 30 minutos | Identificar o esgotamento de recursos |
| Testes de resistência | 1-4 horas | Degradação, estabilidade de latência |
Exploração do ponto de rutura (modo de pesquisa binária)
Para além da varredura gradual (exploration_mode=gradual), o portátil suporta um modo automático de pesquisa binária que encontra o QPS máximo sustentável sem necessidade de especificar manualmente os níveis de concorrência.
Como funciona
Defina exploration_mode=binary_search e especifique max_target_qps (por exemplo, 500). O caderno utiliza a Lei de Little (concurrency = QPS × avg_latency_sec) para converter cada alvo de QPS num nível estimado de concorrência, depois executa uma pesquisa binária da seguinte forma:
- Comece em
max_target_qps / 2(250 no exemplo). - Se a taxa de erro for inferior
error_rate_threshold(sucesso), aumenta o limite inferior e tenta um QPS mais alto (375, depois 500, e assim sucessivamente). - Se a taxa de erro exceder o limiar (falha), baixar o limite superior e tentar a meio caminho entre o último sucesso e o fracasso.
- Repita durante até
exploration_stepspassos (por defeito 8) ou até que o intervalo de pesquisa se reduza a menos de 5% demax_target_qps.
A tabela seguinte mostra como a pesquisa converge para um ponto final hipotético com um ponto de ruptura em torno de 430 QPS:
| Step | Alvo QPS | Taxa de erro | Resultado | Nova gama |
|---|---|---|---|---|
| 1 | 250 | 0,1% | SUCESSO | [250, 500] |
| 2 | 375 | 0,3% | SUCESSO | [375, 500] |
| 3 | 437 | 4.5% | FRACASSO | [375, 437] |
| 4 | 406 | 0,8% | SUCESSO | [406, 437] |
| 5 | 421 | 2.1% | FRACASSO | [406, 421] |
Após 5–8 passos, a pesquisa converge para o ponto de rutura — neste exemplo, aproximadamente 406–421 QPS — com muito menos execuções de teste do que uma varredura exaustiva.
Quando usar cada modo
| Mode | Quando utilizar |
|---|---|
gradual |
Já conhece o alcance operacional esperado e quer caracterizar o desempenho em níveis específicos de concorrência. |
binary_search |
Queres encontrar o QPS máximo sustentável rapidamente, sem conhecer os níveis de concorrência antecipadamente. |
Passo 3. Desenhe o seu conjunto de consultas
Quando possível, o conjunto de consultas deve refletir o tráfego de produção esperado o mais fielmente possível. Especificamente, deve tentar corresponder à distribuição esperada das consultas em termos de conteúdo, complexidade e diversidade.
Use consultas realistas. Não uses texto aleatório como "test query 1234".
Corresponder à distribuição esperada do tráfego de produção. Se espera 80 consultas% comuns, 15% consultas de frequência média e 5 consultas% infrequentes, o seu conjunto de consultas deve refletir essa distribuição.
Corresponde ao tipo de consulta que esperas ver em produção. Por exemplo, se espera que as consultas de produção usem pesquisa híbrida ou filtros, também deve usar esses no seu conjunto de consultas.
Exemplo de consulta usando filtros:
{ "query_text": "wireless headphones", "num_results": 10, "filters": { "brand": "Sony", "noise_canceling": true } }Exemplo de consulta usando pesquisa híbrida:
{ "query_text": "best noise canceling headphones for travel", "query_type": "hybrid", "num_results": 10 }
Diversidade de consultas e cache
Os endpoints de pesquisa vetorial armazenam em cache vários tipos de resultados de consulta para melhorar o desempenho. Esta cache pode afetar os resultados dos testes de carga. Por esta razão, é importante prestar atenção à diversidade do conjunto de consultas. Por exemplo, se enviares repetidamente o mesmo conjunto de consultas, estás a testar a cache, não o desempenho real da pesquisa.
| Utilização: | Quando: | Example |
|---|---|---|
| Idênticas ou poucas consultas |
|
Um widget de recomendação de produto que mostra "itens em tendência" – a mesma consulta é executada milhares de vezes por hora. |
| Consultas diversas |
|
Uma pesquisa de comércio eletrónico onde cada utilizador escreve diferentes pesquisas de produtos. |
Para recomendações adicionais, consulte Resumo das melhores práticas.
Opções para criar um conjunto de consultas
Os separadores de código mostram três opções para criar um conjunto diversificado de consultas. Não existe uma solução única para todos. Escolhe aquele que funcione melhor para ti.
- (Recomendado) Amostragem aleatória da tabela de entrada de índice. Este é um bom ponto de partida geral.
- Amostragem de registos de produção. Este é um bom começo se tiveres registos de produção. Tenha em mente que as consultas normalmente mudam ao longo do tempo, por isso atualize o conjunto de testes regularmente para o manter atualizado.
- Gerar consultas sintéticas. Isto é útil se não tiver registos de produção ou se estiver a usar filtros complexos.
Amostragem aleatória a partir da tabela de entrada
O código seguinte amostra consultas aleatórias da sua tabela de entrada de índice.
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")
Amostra dos registos de produção
Os seguintes exemplos de código são amostrados proporcionalmente a partir de consultas de produção.
# 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)
Consultas sintéticas
Se ainda não tiver registos de produção, pode gerar consultas sintéticas diversas.
# 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")
Passo 4. Teste a sua carga útil
Antes de executar o teste de carga completa, valide a sua carga útil:
- No espaço de trabalho Databricks, navegue até ao seu endpoint de pesquisa vetorial.
- Na barra lateral esquerda, clique em Servir.
- Seleciona o teu endpoint.
- Clique em Usar → Consulta.
- Cole o seu
input.jsonconteúdo na caixa de consulta. - Verifique se o endpoint retorna os resultados esperados.
Isto garante que o seu teste de carga mede consultas realistas, não respostas de erro.
Passo 5. Executar o teste de carga
Verificação de conectividade e aquecimento
Antes do início do teste de carga, o notebook executa dois passos de configuração:
Verificação de conectividade: Envia uma única consulta de teste usando as credenciais do principal do serviço. Se o endpoint devolver um erro 401 ou 403, o notebook falha de imediato com uma mensagem clara
PermissionErrorem vez de realizar um teste de carga completo que apenas produz dados de erro. Isto poupa tempo quando credenciais ou permissões são mal configuradas.Teste de aquecimento (1 minuto): Executa um curto teste de baixa concorrência que aquece os caches dos endpoints e valida o fluxo de pedidos de ponta a ponta. Os resultados do aquecimento não são usados para métricas de desempenho. No modo de pesquisa binária, a latência de aquecimento é também usada como base para a estimativa de concorrência da Lei de Little.
Série principal de testes de carga
O notebook executa uma série de testes com uma concorrência crescente de clientes:
- Início: Baixa concorrência (por exemplo, 5 clientes em simultâneo)
- Meio: Concorrência média (por exemplo, 10, 20 ou 50 clientes)
- Fim: Elevada concorrência (por exemplo, mais de 100 clientes)
Cada teste decorre durante a duração configurada em step_duration_seconds (recomenda-se 5-10 minutos).
O que o portátil mede
O caderno mede e reporta o seguinte:
Métricas de latência:
- P50 (mediana): Metade das consultas são mais rápidas do que isto.
- P95: 95% de consultas são mais rápidas do que isto. Esta é uma métrica chave de SLA.
- P99: 99% de consultas são mais rápidas do que esta.
- Max: Latência do pior cenário.
Métricas de rendimento:
- RPS (pedidos por segundo): Consultas bem-sucedidas por segundo.
- Total de consultas: Número de consultas concluídas.
- Taxa de sucesso: Percentagem de consultas bem-sucedidas.
Erros:
- Falhas de consulta por tipo
- Mensagens de exceção
- Os limites de tempo contam
Armazenamento de resultados
Se o output_table parâmetro estiver definido, o caderno armazena uma linha por nível de concorrência (ou por cada passo de pesquisa binária) numa tabela Delta do Catálogo Unity. A tabela é criada automaticamente na primeira execução e adicionada nas execuções seguintes. Cada linha inclui run_name, exploration_mode, concorrência, taxas de sucesso/falha, percentis de latência, RPS e campos específicos de pesquisa binária (bs_step, bs_target_qps, bs_outcome). Isto permite comparar execuções ao longo do tempo usando ferramentas SQL ou BI.
Executar como um Job Databricks
Todos os parâmetros do notebook são definidos como dbutils.widgets, que correspondem diretamente aos parâmetros de Job do Databricks. Para agendar ou automatizar testes de carga:
- Cria um trabalho com o caderno como tarefa.
- Defina os valores dos widgets como parâmetros de Trabalho. Não são necessárias edições de código.
- Anexe a tarefa a um cluster de nó único, equipado com muitos núcleos de CPU (Locust beneficia de trabalhadores paralelos).
- Executar sob demanda ou com um horário para testes de base recorrentes.
Passo 6. Interpretar resultados
A tabela seguinte mostra as metas para bom desempenho:
| Métrico | Target | Comment |
|---|---|---|
| Latência P95 | < 500ms | A maioria das consultas são rápidas |
| Latência P99 | < 1s | Desempenho razoável em pesquisas de cauda longa |
| Taxa de sucesso | > 99,5% | Baixa taxa de falha |
| Latência ao longo do tempo | Estável | Nenhuma degradação observada durante o teste |
| Consultas por segundo | Atinge o alvo | O endpoint pode lidar com o tráfego esperado |
Os seguintes resultados indicam um desempenho fraco:
- P95 > 1s. Indica que as consultas são demasiado lentas para utilização em tempo real.
- P99 > 3s. A latência nas consultas long-tail prejudica a experiência do utilizador.
- Taxa < de sucesso 99%. Demasiados fracassos.
- Latência crescente. Indica esgotamento de recursos ou fuga de memória.
- Erros de limitação de taxa (429). Indica que é necessária uma maior capacidade do endpoint.
Compensação entre RPS e latência
O RPS máximo não é o ponto ótimo para a taxa de produção. A latência aumenta de forma não linear à medida que se aproxima do débito máximo. Operar ao máximo de RPS resulta frequentemente numa latência 2-5x superior comparado com operar a 60-70% da capacidade máxima.
O exemplo seguinte mostra como analisar os resultados para encontrar o ponto operacional ótimo.
- O RPS máximo é de 480 em 150 clientes concorrentes.
- O ponto operacional ideal é 310 RPS com 50 clientes simultâneos (capacidade de 65%).
- A penalização de latência no máximo: P95 é 4,3x superior (1,5s vs. 350ms)
- Neste exemplo, a recomendação é dimensionar o endpoint para uma capacidade de 480 RPS e operar a ~310 RPS.
| Concurrency | P50 | Pág. 95 | Pág. 99 | RPS | Sucesso | Capacidade |
|---|---|---|---|---|---|---|
| 5 | 80ms | 120ms | 150 milissegundos | 45 | 100% | 10% |
| 10 | 85ms | 140ms | 180ms | 88 | 100% | 20% |
| 20 | 95ms | 180ms | 250 milissegundos | 165 | 99,8% | 35% |
| 50 | 150 milissegundos | 350ms | 500 ms | 310 | 99,2% | 65% ← Ponto ideal |
| 100 | 250 milissegundos | 800ms | 1.2s | 420 | 97,5% | 90% ⚠️ Aproximando-se do máximo |
| 150 | 450ms | 1,5s | 2,5s | 480 | 95.0% | 100% ❌ Máximo de RPS |
Operar ao máximo RPS pode levar aos seguintes problemas:
- Degradação da latência. No exemplo, o P95 tem 350 ms a 65% de capacidade, mas é 1,5 s a 100% de capacidade.
- Não há espaço para acomodar picos de tráfego ou picos de carga. Com 100% capacidade, qualquer pico provoca um timeout. Com uma capacidade de 65%, um pico de tráfego de 50% pode ser gerido sem problema.
- Aumento das taxas de erro. No exemplo, a taxa de sucesso é de 99,2% com capacidade de 65%, mas 95,0% — uma taxa de falha de 5% — a 100% de capacidade.
- Risco de esgotamento de recursos. Na carga máxima, as filas aumentam, a pressão da memória aumenta, os pools de ligação começam a saturar-se e o tempo de recuperação após incidentes aumenta.
A tabela seguinte mostra os pontos de funcionamento recomendados para diferentes casos de uso.
| Caso de uso | Capacidade alvo | Fundamentação |
|---|---|---|
| Sensível à latência (pesquisa, chat) | 50-60% do máximo | Priorize a baixa latência P95/P99 |
| Equilibrado (recomendações) | 60-70% de máximo | Bom equilíbrio entre custo e latência |
| Otimização de custos (tarefas em lote) | 70-80% de máximo | Latência superior aceitável |
| Não recomendado | > 85% de máximo | Picos de latência, sem capacidade de rajada |
Funções auxiliares para calcular o tamanho do ponto operacional e do ponto final
Encontrar o ponto ótimo
O código seguinte representa a latência QPS vs P95. No gráfico, procure o ponto onde a curva começa a curvar-se bruscamente para cima. Este é o ponto operacional ideal.
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()
Fórmula de recomendação de tamanho
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
Identificar o gargalo do modelo de embedding
Se o seu índice usar embeddings geridos, o caderno de testes de carga mede os tempos de cada componente através do parâmetro debug_level=1 em cada consulta. A tabela de resultados inclui:
-
ann_time— tempo gasto na pesquisa aproximada do vizinho mais próximo -
embedding_gen_time— tempo gasto a gerar o vetor de consulta no endpoint de serviço do modelo -
reranker_time— tempo gasto na reclassificação (se ativado) -
response_time— tempo total de resposta de ponta a ponta
Se embedding_gen_time for consistentemente grande em relação a ann_time, o ponto final de embedding é o gargalo, não o ponto final da pesquisa vetorial. Causas comuns:
- O modelo de embedding que serve o endpoint tem o Scale to Zero ativado. Desative-o para testes de carga em ambiente de produção. Consulte Evitar a escalagem para zero na produção.
- O ponto final de incorporação não tem concorrência predefinida suficiente para a taxa de consulta em teste.
- O endpoint do modelo de embedding é partilhado com outras cargas de trabalho. Use um endpoint dedicado para testes de carga.
Sugestão
Para isolar o desempenho da pesquisa vetorial do desempenho do modelo de embedding, opte por embeddings autogeridos para testes de carga. Passa vetores pré-computados no EMBEDDING_COLUMN parâmetro em vez de consultas de texto. Isto elimina completamente a latência de embedding da medição.
Passo 7: Dimensione o seu endpoint
Usa a recomendação do caderno
Após analisar os resultados, o caderno pede-lhe para:
- Seleciona a linha que melhor corresponde aos teus requisitos de latência.
- Introduza o RPS desejado pela sua aplicação.
O caderno mostra então o tamanho recomendado do endpoint. Calcula a capacidade necessária com base no seguinte:
- O teu RPS alvo
- Latência observada em diferentes níveis de concorrência
- Limiares de taxa de sucesso
- Margem de segurança (tipicamente 2x carga máxima esperada)
Considerações sobre o dimensionamento
Endpoints padrão:
- Escala automaticamente para suportar o tamanho do índice
- Escalar manualmente para suportar a taxa de transferência
- Reduza automaticamente a escala para baixo quando os índices são eliminados
- Reduza manualmente a escala para reduzir a capacidade
Endpoints otimizados para armazenamento:
- Escala automaticamente para suportar o tamanho do índice
- Reduza automaticamente a escala para baixo quando os índices são eliminados
Passo 8: Validar a Configuração Final
Após atualizar a configuração do seu endpoint:
- Espera que o endpoint esteja pronto. Isto pode demorar vários minutos.
- Executa o teste final de validação no caderno.
- Confirme que o desempenho cumpre os seus requisitos:
- RPS ≥ taxa de transferência objetivo
- A latência P95 cumpre os SLA
- Taxa > de sucesso 99,5%
- Sem erros sustentados
Se a validação falhar, tente o seguinte:
- Aumentar a capacidade do endpoint
- Otimizar a complexidade das consultas
- Revisão do desempenho do filtro
- Verifique a configuração do endpoint de incorporação
Quando voltar a testar
Para manter a visibilidade de desempenho, é aconselhável fazer testes de carga base trimestrais. Deve também repetir o teste sempre que fizer alguma das seguintes alterações:
- Alterar padrões de consulta ou complexidade
- Atualizar o índice de pesquisa vetorial
- Modificar configurações de filtros
- Espere aumentos significativos de tráfego
- Implementar novas funcionalidades ou otimizações
- Mudança de tipos de endpoint padrão para tipos de endpoint otimizados para armazenamento
Troubleshooting
Todos os pedidos falham com ~10ms de latência e respostas de 240 bytes
Isto indica que o principal do serviço está a receber uma resposta 401/403. Verificar:
- O principal do serviço tem permissões Pode Consultar no endpoint de pesquisa vetorial (não apenas no índice).
- O âmbito secreto contém chaves válidas
service_principal_client_ideservice_principal_client_secret. - O segredo OAuth não expirou.
O portátil inclui uma verificação de conectividade que deteta isto antes de executar o teste de carga completa.
Executar múltiplos trabalhos de teste de carga no mesmo cluster
Se executares dois trabalhos de teste de carga em simultâneo no mesmo cluster, um dos trabalhos pode receber tokens OAuth obsoletos ou ter conflitos de CPU com os trabalhadores Locust do outro. Para resultados fiáveis, execute trabalhos de teste de carga um de cada vez num cluster dedicado.
Os grafos de temporização dos componentes estão vazios
Os grafos de temporização de componentes (ann_time, embedding_gen_time, reranker_time) exigem que o endpoint devolva debug_info nas respostas de consulta. Se estes grafos estiverem vazios:
- Verifique se está a utilizar o script
fast_vs_load_test_async_load.py(que analisadebug_infoa partir das respostas) como olocust_script_path. - Algumas configurações de endpoint podem não devolver
debug_info. Os índices de embedding autogeridos normalmente retornamann_timeeresponse_timemas nãoembedding_gen_timeoureranker_time.
Tabela de resultados não consultável a partir de um SQL warehouse
O caderno escreve os resultados da sessão Spark do cluster. Se um SQL warehouse mostrar 0 linhas para uma tabela que o caderno reporta como preenchida, o problema pode ser um atraso na sincronização de metadados do Unity Catalog. Espere alguns minutos e tente novamente, ou consulte a tabela diretamente a partir de um caderno ligado ao mesmo cluster.
Resumo das melhores práticas
Configuração de teste
Faça testes durante pelo menos 5 minutos na carga máxima.
Use princípios de serviço OAuth para autenticação.
Crie cargas úteis de consulta realistas que correspondam às consultas de produção esperadas.
Teste com filtros e parâmetros semelhantes aos de produção.
Inclua um período de aquecimento antes de medir.
Teste em múltiplos níveis de concorrência.
Acompanhe as latências P95/P99, não apenas as médias.
Teste o desempenho tanto em cache como sem 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)")
Desenho de conjuntos de consultas
- Ajuste a diversidade das suas consultas de teste com a distribuição real de tráfego (consultas frequentes e raras).
- Use consultas reais dos registos (anonimizadas).
- Inclua diferentes complexidades de consulta.
- Testa ambos os cenários em cache e sem cache e acompanha os resultados separadamente.
- Teste com as combinações de filtros esperadas.
- Usa os mesmos parâmetros que vais usar em produção. Por exemplo, se usar pesquisa híbrida em produção, inclua consultas de pesquisa híbrida. Use um parâmetro semelhante
num_resultscomo na produção. - Não uses consultas que nunca ocorrerão em produção.
Otimização do desempenho
Se as latências forem demasiado elevadas, experimente o seguinte:
- Use princípios de serviço OAuth (não PATs) - melhoria de 100ms
- Reduzir
num_results- Obter 100 resultados é mais lento do que 10 - Otimizar filtros - Filtros complexos ou excessivamente restritivos abrandam as consultas
- Verifica o endpoint de embedding - Certifica-te de que não está escalado para zero ou que tem largura de banda suficiente
Se estiver a atingir os limites de taxa, experimente o seguinte:
- Aumentar a capacidade do endpoint - Ampliar o seu endpoint
- Implementar limitação de taxa do lado do cliente ou distribuir consultas ao longo do tempo
- Utilize agrupamento de ligações - Reutilize as ligações
- Adicionar lógica de nova tentativa - Usar retrocesso exponencial (que já é parte do SDK Python)