Compreendendo e Ajustando o Tamanho do Heap JVM do Elasticsearch para Desempenho

Orientação prática para dimensionar o heap JVM do Elasticsearch, interpretar sintomas de GC e evitar configurações de memória que prejudicam o desempenho da busca.

Compreendendo e Ajustando o Tamanho do Heap JVM do Elasticsearch para Desempenho

O tamanho do heap JVM do Elasticsearch é uma daquelas configurações que as pessoas costumam ajustar cedo demais e culpar tarde demais. Um cluster lento nem sempre precisa de mais heap. Às vezes, o oposto é verdadeiro: o heap é grande o suficiente, mas o sistema operacional tem pouca memória restante para o cache do sistema de arquivos, então o Lucene precisa acessar o disco com mais frequência. Outras vezes, o heap é genuinamente pequeno demais, a coleta de lixo está rodando constantemente, e cada busca parece estar passando por cimento molhado.

O objetivo prático não é encontrar um número mágico. O objetivo é dar ao Elasticsearch heap suficiente para metadados do cluster, buffers de indexação, agregações, trabalho de consulta e caches, enquanto deixa RAM suficiente fora do heap para arquivos de segmento do Lucene e o sistema operacional. Se você se lembrar de apenas uma coisa, lembre-se de que o desempenho do Elasticsearch depende tanto da memória dentro quanto fora do heap.

Comece separando dois tipos de pressão de memória. A pressão no heap JVM aparece como heap.percent alto, pausas longas de coleta de lixo, exceções do circuit breaker pai, pressão de fielddata ou OutOfMemoryError. A pressão fora do heap geralmente parece buscas lentas mesmo que o heap pareça bem, leituras de disco altas, atividade de swap ou baixas taxas de acerto de cache após uma reinicialização. Aumentar o heap pode ajudar no primeiro caso. Pode piorar o segundo caso.

Para a maioria dos clusters autogerenciados, a velha regra prática ainda funciona como ponto de partida: defina o heap para não mais da metade da RAM da máquina e mantenha-o abaixo do limite de ponteiros de objetos comuns comprimidos. As pessoas frequentemente citam esse limite como "cerca de 32 GB". Na prática, muitos operadores ficam em torno de 26-31 GB porque o corte exato depende da JVM e do layout de tempo de execução. O Elasticsearch registra se ponteiros de objetos comuns comprimidos estão habilitados na inicialização. Trate o log de inicialização como a fonte da verdade para o seu nó.

Nas versões modernas do Elasticsearch, o dimensionamento automático do heap pode já definir um valor razoável com base nas funções do nó e na memória disponível. Isso é útil, especialmente para clusters menores e implantações padrão. O ajuste manual ainda importa quando um nó tem uma carga de trabalho incomum: agregações pesadas, mapeamentos grandes, muitos shards, pipelines de ingestão, trabalhos de transformação, funções de aprendizado de máquina ou uma mistura de busca intensa e alto volume de indexação.

Aqui está um exemplo simples. Suponha que você tenha um nó de dados quente de 64 GB que lida tanto com indexação quanto com busca. Um heap de 30 GB é um ponto de partida comum. Ele deixa aproximadamente metade da RAM para o cache de página do SO e memória nativa. Se o mesmo nó faz parte de um cluster de logging com muitos shards pequenos e agregações de alta cardinalidade, você ainda pode ver pressão no heap. A correção pode ser um melhor design de shard ou limpeza de mapeamento, não automaticamente um heap de 40 GB. Mover-se acima do limite de ponteiro comprimido pode aumentar o tamanho do ponteiro do objeto e reduzir a eficiência efetiva do heap.

Defina o heap mínimo e máximo para o mesmo valor. O Elasticsearch não deve gastar tempo redimensionando o heap enquanto está servindo tráfego.

-Xms30g
-Xmx30g

Use um arquivo em jvm.options.d/ em vez de editar o arquivo jvm.options empacotado diretamente quando sua instalação suportar. Para instalações de pacote, isso geralmente significa algo como /etc/elasticsearch/jvm.options.d/heap.options. Para Docker, passe as configurações de heap através da variável de ambiente suportada ou configuração montada. Mantenha a configuração consistente para nós com a mesma função, mas não presuma que toda função precisa do mesmo heap. Nós dedicados elegíveis a mestre geralmente precisam de muito menos heap do que nós de dados ocupados, a menos que o cluster tenha metadados muito grandes devido a muitos índices, campos ou shards.

Antes de alterar o heap, tire um instantâneo do comportamento atual. Veja as estatísticas da JVM:

GET _nodes/stats/jvm?filter_path=nodes.*.jvm.mem,nodes.*.jvm.gc

Em seguida, verifique os circuit breakers e fielddata:

GET _nodes/stats/breaker,indices/fielddata?pretty

Também verifique shards e mapeamentos. Um cluster com milhares de shards minúsculos pode queimar heap em sobrecarga mesmo quando o volume de dados não é enorme.

GET _cat/shards?v&bytes=gb
GET _cluster/stats?filter_path=indices.count,indices.shards,indices.mappings

Os sintomas importam mais do que o número principal. Heap em torno de 70-85% durante picos pode ser normal se a coleta de lixo o reduzir rapidamente. Heap que sobe para os altos 90% e fica lá é diferente. Pausas longas de GC da geração antiga são piores do que uma porcentagem alta por si só. Se as buscas expiram sempre que o GC roda, os usuários não se importam que o gráfico médio de heap parecia aceitável.

Um erro comum de produção é usar o heap como um curativo para mapeamentos ruins. Classificar ou agregar em campos text analisados com fielddata: true pode consumir muito heap. A correção melhor geralmente é um subcampo keyword, um campo de cardinalidade mais baixa ou um design de agregação diferente. Outro erro é permitir que mapeamentos dinâmicos criem milhares de campos a partir de chaves JSON arbitrárias. O heap então desaparece em mapeamentos, estado do cluster e estruturas de consulta. Coloque limites em campos dinâmicos antes que eles se tornem um problema operacional.

Agregações merecem atenção especial. Uma agregação de termos em um campo de alta cardinalidade pode criar grandes estruturas em memória. Se alguém executa um painel que agrupa por user_id, session_id ou URL completo em um longo intervalo de tempo, a pressão no heap pode aumentar mesmo que as buscas comuns pareçam boas. Use janelas de tempo menores, filtros que estreitam o conjunto de trabalho, agregação composite para paginação ou rollups pré-agregados quando apropriado. Aumentar o heap pode atrasar a falha, mas não tornará uma agregação ilimitada barata.

A indexação tem seu próprio padrão de heap. Requisições em lote criam pressão transitória. Payloads de lote muito grandes podem forçar o Elasticsearch a segurar muito trabalho de uma vez. Se você vir rejeições de indexação ou picos de heap durante a ingestão, tente lotes menores e mais controle de concorrência no lado do cliente. Um intervalo inicial útil é frequentemente alguns megabytes por requisição em lote, depois teste para cima com seus documentos reais. O valor certo depende do tamanho do documento, pipelines de ingestão, réplicas, intervalo de refresh e velocidade de armazenamento.

Não ignore o sistema operacional. Desabilite swap ou configure o host para que a memória do Elasticsearch não seja trocada. Swapping pode transformar um problema de memória recuperável em uma longa paralisação porque as pausas da JVM se tornam enormes. Certifique-se de que o processo tenha permissão para travar a memória se você usar bootstrap.memory_lock: true, e verifique na inicialização em vez de assumir que a configuração funcionou.

Após alterar o heap, reinicie um nó de cada vez em um cluster de produção e aguarde a recuperação do shard se estabilizar antes de passar para o próximo nó. Observe a latência de busca, pausas de GC, leituras de disco e throughput de indexação por pelo menos um ciclo normal de tráfego. Uma mudança que parece boa durante uma hora tranquila pode falhar durante o pico matinal do painel.

Se um nó continua atingindo pressão no heap após um ajuste sensato, recue e pergunte o que o heap está segurando. Muitos shards, muitos campos, fielddata em texto, agregações superdimensionadas, scripts pesados e funções mistas no mesmo nó são causas mais comuns do que "Elasticsearch precisa de toda a RAM". O trabalho de ajuste de heap mais forte muitas vezes termina com um mapeamento menor, menos shards, melhores limites de consulta ou uma camada de ingestão dedicada.

O ajuste de heap não é uma cerimônia única. Faz parte do gerenciamento de capacidade. Quando o volume de dados cresce, os painéis mudam ou uma nova equipe começa a enviar documentos mais amplos, o perfil de memória também muda. Mantenha o dimensionamento do heap chato: meça, mude uma coisa, reinicie com segurança e verifique com dados de carga de trabalho reais.