Otimizando Consultas Lentas no Elasticsearch: Melhores Práticas para Ajuste de Desempenho
Diagnostique e melhore consultas lentas no Elasticsearch com melhor formato de consulta, paginação, cache, mapeamentos e a API de Perfil.
Otimizando Consultas Lentas no Elasticsearch: Melhores Práticas para Ajuste de Desempenho
Consultas lentas no Elasticsearch geralmente vêm de um de quatro lugares: a consulta pede demais, o mapeamento torna a consulta cara, o cluster está com recursos escassos ou a aplicação repete buscas custosas que deveriam ser armazenadas em cache ou redesenhadas. A correção depende de qual delas é verdadeira.
Antes de reescrever tudo, capture uma requisição lenta real com seu índice, filtros, ordenação, agregações, profundidade de página, tamanho da resposta e tempo. Uma agregação de dashboard, uma consulta de autocomplete e um trabalho de exportação estressam o Elasticsearch de maneiras diferentes.
Entendendo os Gargalos de Desempenho de Consultas
Antes de mergulhar nas soluções, é útil entender as razões comuns por trás de consultas lentas no Elasticsearch. Elas geralmente incluem:
- Consultas Complexas: Consultas com múltiplas cláusulas
bool, consultas aninhadas ou operações caras comowildcardouregexpem grandes conjuntos de dados. - Recuperação Ineficiente de Dados: Buscar
_sourcedesnecessariamente ou recuperar grandes números de documentos para paginação. - Restrições de Recursos: CPU, memória ou I/O de disco insuficientes nos nós de dados.
- Mapeamentos Subótimos: Usar tipos de dados incorretos ou não aproveitar
doc_valuespara agregações. - Desequilíbrio ou Sobrecarga de Shards: Muitos shards, poucos shards ou distribuição desigual de shards/dados.
- Perda de Cache ou Ajuste Ruim de Cache: Repetir buscas caras sem usar cache de requisição, contexto de filtro ou cache em nível de aplicação quando apropriado.
Otimizando a Estrutura da Consulta
A maneira como você constrói suas consultas tem um impacto profundo em seu desempenho. Pequenas mudanças podem levar a melhorias significativas.
1. Recupere Apenas os Campos Necessários (Filtragem _source e stored_fields)
Por padrão, o Elasticsearch retorna todo o campo _source para cada documento correspondente. Se seus documentos são grandes e a interface precisa apenas de um título, ID e timestamp, buscar o documento inteiro desperdiça largura de banda de rede e tempo de análise.
Filtragem
_source: Use o parâmetro_sourcepara especificar um array de campos a incluir ou excluir.GET /meu-indice/_search { "_source": ["titulo", "autor", "data_publicacao"], "query": { "match": { "conteudo": "desempenho Elasticsearch" } } }stored_fields: Se você armazenou explicitamente campos específicos em seu mapeamento ("store": true), pode recuperá-los comstored_fields. A maioria das implantações não armazena muitos campos dessa forma, então a filtragem_sourceé a correção mais comum.GET /meu-indice/_search { "stored_fields": ["titulo", "autor"], "query": { "match": { "conteudo": "desempenho Elasticsearch" } } }
2. Prefira Tipos de Consulta Eficientes
Alguns tipos de consulta são inerentemente mais intensivos em recursos do que outros.
Evite Wildcards à Esquerda e Regexps Amplos: Consultas
wildcarderegexppodem ser caras, especialmente com wildcards à esquerda como*teste. Consultas de prefixo são geralmente mais gerenciáveis do que buscas com wildcard à esquerda, mas ainda precisam de mapeamentos sensatos e entrada limitada.# Ineficiente - evite wildcard à esquerda { "query": { "wildcard": { "nome.keyword": { "value": "*busca" } } } } # Melhor - se você conhece o prefixo { "query": { "prefix": { "nome.keyword": { "value": "Elastic" } } } }Use
match_phrasepara intenção de frase: Se o usuário está buscando uma frase exata,match_phraseexpressa essa intenção melhor do que várias cláusulasmatchnão relacionadas. Nem sempre é mais barato, mas evita retornar documentos que contêm as palavras muito distantes.Contexto de filtro para condições sim/não: Quando você só se importa se um documento corresponde a uma condição, coloque essa condição no contexto
filterou useconstant_score. Isso evita trabalho de pontuação desnecessário e é mais amigável ao cache.GET /meu-indice/_search { "query": { "constant_score": { "filter": { "term": { "status": "ativo" } } } } }
3. Otimize Consultas Booleanas
- Use filtros para restrições estruturadas: Coloque IDs de tenant, valores de status, intervalos de data e tags exatas em
filter, não emmust, a menos que precisem de pontuação. O Elasticsearch pode reordenar e otimizar cláusulas internamente, então não confie na ordem JSON como sua principal ferramenta de desempenho. - Use
minimum_should_matchintencionalmente: Pode melhorar a relevância e reduzir correspondências amplas, mas configurá-lo muito alto pode ocultar resultados válidos.
4. Paginação Eficiente (search_after e scroll)
A paginação tradicional from/size torna-se muito ineficiente para páginas profundas (por exemplo, from: 10000, size: 10). O Elasticsearch tem que recuperar e ordenar todos os documentos até from + size em cada shard, depois descartar from documentos.
search_after: Para paginação profunda em tempo real,search_afteré recomendado. Ele usa a ordem de ordenação do último documento da página anterior para encontrar o próximo conjunto de resultados, semelhante a cursores em bancos de dados tradicionais. É sem estado e escala melhor.# Primeira requisição GET /meu-indice/_search { "size": 10, "query": {"match_all": {}}, "sort": [{"timestamp": "asc"}, {"_id": "asc"}] } # Requisição subsequente usando os valores de ordenação do último documento da primeira requisição GET /meu-indice/_search { "size": 10, "query": {"match_all": {}}, "search_after": [1678886400000, "doc_id_XYZ"], "sort": [{"timestamp": "asc"}, {"_id": "asc"}] }API
scroll: Para recuperação em massa de grandes conjuntos de dados, como reindexação ou exportações,scrollainda pode ser útil. Para versões mais recentes do Elasticsearch e varreduras completas de índice de longa duração, considere também point-in-time maissearch_after. Scroll não é adequado para paginação em tempo real voltada ao usuário.
5. Otimizando Agregações
Agregações podem ser intensivas em recursos, especialmente em campos de alta cardinalidade.
- Pré-computando Agregações: Considere executar agregações complexas e não em tempo real durante a indexação ou em um cronograma para pré-computar resultados e armazená-los em um índice separado.
doc_values: Garanta que campos usados em agregações tenhamdoc_valueshabilitados (que é o padrão para a maioria dos campos não-texto). Isso permite que o Elasticsearch carregue dados para agregações de forma eficiente sem carregar_source.eager_global_ordinals: Para camposkeywordfrequentemente usados em agregaçõesterms, configurareager_global_ordinals: trueno mapeamento pode melhorar o desempenho pré-construindo ordinais globais. Isso incorre em um custo no momento da atualização do índice, mas acelera as agregações em tempo de consulta.
Aproveitando Técnicas de Cache
O Elasticsearch oferece várias camadas de cache que podem acelerar significativamente consultas repetidas.
1. Cache de Consulta do Nó
- Mecanismo: Armazena em cache os resultados de cláusulas de filtro dentro de consultas
boolque são usadas com frequência. É um cache em memória no nível do nó. - Eficácia: Mais eficaz para cláusulas de filtro repetidas. Não conte com ele para todas as consultas; o Elasticsearch decide o que vale a pena armazenar em cache.
- Configuração: Habilitado por padrão. Você pode controlar seu tamanho com
indices.queries.cache.size(padrão 10% do heap).
2. Cache de Requisição do Shard
Mecanismo: Armazena em cache resultados de busca em nível de shard, mais comumente para requisições pesadas em agregações com
size=0. É um ajuste forte para consultas de dashboard repetidas sobre dados que não estão mudando a cada segundo.Eficácia: Excelente para consultas de dashboard ou aplicações analíticas onde a mesma requisição (incluindo agregações) é executada repetidamente com parâmetros idênticos.
Como usar: Habilite-o explicitamente em sua consulta usando
"request_cache": true.GET /meu-indice/_search?request_cache=true { "size": 0, "query": { "bool": { "filter": [ {"term": {"status.keyword": "ativo"}}, {"range": {"timestamp": {"gte": "now-1h"}}} ] } }, "aggs": { "mensagens_por_minuto": { "date_histogram": { "field": "timestamp", "fixed_interval": "1m" } } } }Ressalvas: O cache é invalidado sempre que um shard é atualizado (novos documentos são indexados ou existentes são atualizados). Útil apenas para consultas que retornam resultados idênticos com frequência.
3. Cache do Sistema de Arquivos (Nível do SO)
- Mecanismo: O cache do sistema de arquivos do sistema operacional desempenha um papel crítico. O Elasticsearch depende fortemente dele para armazenar em cache segmentos de índice acessados com frequência.
- Eficácia: Crucial para o desempenho de consultas. Se os segmentos de índice estão na RAM, o I/O de disco é completamente evitado, levando a uma execução de consulta muito mais rápida.
- Melhor Prática: Deixe RAM substancial para o cache do sistema de arquivos. Um ponto de partida comum é manter o heap JVM em torno da metade da memória do sistema, com os limites usuais de heap do Elasticsearch em mente, depois valide com sua carga de trabalho.
4. Cache em Nível de Aplicação
- Mecanismo: Implementar um cache na camada da sua aplicação (por exemplo, usando Redis, Memcached ou um cache em memória) para resultados de busca frequentemente solicitados.
- Eficácia: Pode fornecer os tempos de resposta mais rápidos ao evitar completamente o Elasticsearch para requisições repetidas. Melhor para resultados de busca estáticos ou que mudam lentamente.
- Considerações: A estratégia de invalidação de cache é chave. Requer design cuidadoso para garantir consistência de dados.
Usando a API de Perfil para Identificação de Gargalos
A API de Perfil é uma ferramenta inestimável para entender exatamente como o Elasticsearch executa uma consulta e onde o tempo é gasto. Ela detalha o tempo de execução para cada componente de sua consulta e agregação.
Como Usar a API de Perfil
Simplesmente adicione "profile": true ao corpo da sua requisição de busca.
GET /meu-indice/_search
{
"profile": true,
"query": {
"bool": {
"must": [
{"match": {"titulo": "Elasticsearch"}},
{"term": {"status.keyword": "publicado"}}
],
"filter": [
{"range": {"data_publicacao": {"gte": "2023-01-01"}}}
]
}
},
"aggs": {
"principais_autores": {
"terms": {
"field": "autor.keyword",
"size": 10
}
}
}
}
Interpretando Resultados da API de Perfil
A resposta incluirá uma seção profile detalhando a execução da consulta e agregação em cada shard. Métricas chave a procurar incluem:
description: O componente específico da consulta ou agregação.time_in_nanos: O tempo gasto executando este componente.breakdown: Sub-métricas detalhadas comobuild_scorer_time,collect_time,set_weight_timepara consultas ereduce_timepara agregações.children: Componentes aninhados, mostrando como o tempo é distribuído dentro de consultas complexas.
Exemplo de Interpretação:
Se você vir um alto time_in_nanos para um WildcardQuery, confirma que esta é uma parte cara da sua consulta. Se collect_time é alto, sugere que recuperar e processar documentos após uma correspondência é um gargalo, possivelmente devido à análise de _source ou paginação profunda. Alto reduce_time em agregações pode indicar uma carga pesada durante a fase final de mesclagem.
Ao examinar essas métricas, você pode identificar cláusulas de consulta específicas ou campos de agregação que estão consumindo mais recursos e então aplicar as técnicas de otimização discutidas anteriormente.
Melhores Práticas Gerais para Desempenho
Além de otimizações específicas de consulta, várias práticas recomendadas em todo o cluster e nível de índice contribuem para o desempenho geral da busca.
1. Mapeamentos de Índice Ótimos
textvs.keyword: Usetextpara busca de texto completo ekeywordpara correspondência de valor exato, ordenação e agregações. Tipos incompatíveis podem levar a consultas ineficientes.doc_values: Garanta quedoc_valuesestejam habilitados para campos que você pretende ordenar ou agregar. Eles estão habilitados por padrão para a maioria dos tipos de campo que suportam ordenação e agregações, como camposkeyword, numéricos, de data, booleanos e IP. Campostextsimples são para busca de texto completo; use um subcampokeywordquando precisar de correspondência exata ou agregação.norms: Desabilitenorms("norms": false) para campos onde você não precisa de normalização de comprimento de documento (por exemplo, campos de ID). Isso economiza espaço em disco e melhora a velocidade de indexação, com impacto mínimo no desempenho de consultas para consultas sem pontuação.index_options: Para campostext, useindex_options: docsse você só precisa saber se um termo existe em um documento, eindex_options: positions(o padrão) se você precisa de consultas de frase e buscas de proximidade.
2. Monitore a Saúde do Cluster e Recursos
- Status do Cluster: Verde é o objetivo. Amarelo significa que um ou mais shards de réplica não estão atribuídos; as buscas ainda podem funcionar, mas a resiliência é reduzida e o desempenho pode sofrer. Vermelho significa que shards primários estão faltando e alguns dados estão indisponíveis.
- Monitoramento de Recursos: Monitore regularmente CPU, RAM, I/O de disco e uso de rede em seus nós de dados. Picos nessas métricas geralmente se correlacionam com consultas lentas.
- Heap JVM: Fique de olho no uso do heap JVM. Alta utilização pode levar a pausas frequentes de coleta de lixo, tornando as consultas lentas. Otimize consultas para reduzir a pressão no heap.
3. Alocação Adequada de Shards
- Muitos Shards: Cada shard consome recursos. Muitos shards pequenos criam sobrecarga. Shards na casa das dezenas de gigabytes são comuns, mas o tamanho certo depende do heap, padrão de consulta, metas de recuperação e hardware.
- Poucos Shards: Limita o paralelismo. Consultas contra um índice com poucos shards não conseguirão aproveitar todos os nós de dados disponíveis de forma eficiente.
4. Estratégia de Indexação
- Intervalo de Atualização: Um
refresh_intervalmais baixo (padrão 1 segundo) torna os dados visíveis mais rapidamente, mas aumenta a sobrecarga de indexação. Para cargas de trabalho pesadas em busca, considere aumentá-lo ligeiramente (por exemplo, 5-10 segundos) para reduzir a pressão de atualização.
O fluxo de trabalho prático é simples: encontre a consulta lenta real, perfile-a, reduza a quantidade de dados que ela toca e faça o mapeamento corresponder à maneira como os usuários buscam. Se a consulta já está limpa, olhe para o layout do shard, pressão no heap, cache do sistema de arquivos e I/O de disco. O Elasticsearch é rápido quando o design do índice, o formato da consulta e os recursos do cluster concordam entre si.