Engenharia

Quantos shards eu devo ter em meu cluster do Elasticsearch?

Nota do editor: A regra prática “Procurar manter até 20 shards por GB de memória heap” foi descontinuada na versão 8.3. Este post do blog foi atualizado para refletir a nova recomendação.

O Elasticsearch é uma plataforma bastante versátil que oferece suporte para vários casos de uso, além de proporcionar excelente flexibilidade nas estratégias de organização e replicação de dados. Entretanto, essa flexibilidade às vezes dificulta a determinação antecipada de como organizar melhor os dados em índices e shards, principalmente se você estiver começando agora a trabalhar com o Elastic Stack. Apesar de escolhas inferiores às ideais não necessariamente causarem problemas logo de início, elas têm o potencial de causar problemas de desempenho à medida que os volumes de dados vão aumentando com o tempo. Quanto mais dados o cluster contiver, mais difícil também ficará a solução do problema, uma vez que, às vezes, a reindexação de grandes quantidades de dados pode ser necessária.

Quando nos deparamos com usuários que enfrentam problemas de desempenho, não é incomum que isso possa ser rastreado de volta a problemas em torno de como os dados estão indexados e do número de shards no cluster. Isso é particularmente verdadeiro para casos de uso envolvendo multitenancy e/ou uso de índices baseados em tempo. Ao discutir essa questão com os usuários, seja pessoalmente, em eventos, encontros ou por meio do nosso fórum, algumas das dúvidas mais comuns são “Quantos shards devo ter?” e “Qual é o tamanho que os shards devem ter?”.

Este post do blog tem como objetivo ajudar você a responder essas dúvidas e fornecer orientações práticas para casos de uso que envolvem o uso de índices baseados em tempo (por exemplo, logging ou análise de segurança) em um único local.

O que é um shard?

Antes de começarmos, precisamos estabelecer alguns fatos e terminologia de que precisaremos em seções mais adiante.

Os dados no Elasticsearch estão organizados em índices. Cada índice consiste em um ou mais shards. Cada shard é uma instância de um índice de Lucene, que pode ser considerado um mecanismo de pesquisa autocontido que indexa e manipula consultas para um subconjunto dos dados em um cluster do Elasticsearch.

À medida que os dados são gravados em um shard, eles são publicados periodicamente em novos segmentos Lucene imutáveis no disco, e é nessa hora que ficam disponíveis para consultas. Isso é denominado atualização. O modo de funcionamento disso é descrito em mais detalhes em Elasticsearch: the Definitive Guide (Guia definitivo do Elasticsearch).

À medida que cresce o número de segmentos, estes são consolidados periodicamente em segmentos maiores. Esse processo é denominado mesclagem. Como todos os segmentos são imutáveis, isso significa que o espaço em disco usado normalmente oscilará durante a indexação, à medida que novos segmentos mesclados precisem ser criados antes que os substituídos possam ser excluídos. A mesclagem pode usar muitos recursos, principalmente com relação a E/S de disco.

O shard é a unidade em que o Elasticsearch distribui dados em torno do cluster. A velocidade em que o Elasticsearch pode mover shards ao rebalancear dados, por exemplo, após uma falha, dependerá do tamanho e do número de shards, além do desempenho da rede e do disco.

DICA: evite ter shards muito grandes, pois isso pode prejudicar a capacidade de recuperação do cluster após uma falha. Não há limite fixo para o tamanho máximo dos shards, mas um tamanho de 50 GB é citado como limite que costuma funcionar para vários casos de uso.

Índice por período de retenção

Como os segmentos são imutáveis, atualizar um documento exige que o Elasticsearch primeiro encontre o documento existente, marque-o como excluído e adicione a versão atualizada. Excluir um documento também exige que o documento seja encontrado e marcado como excluído. Por isso, os documentos excluídos continuarão ocupando espaço em disco e alguns recursos de sistema até que sejam mesclados, o que pode consumir mais recursos do sistema.

O Elasticsearch permite que índices completos sejam excluídos de forma muito eficiente diretamente do sistema de arquivos, sem explicitamente obrigar a exclusão de cada um dos registros. Essa é a maneira mais eficiente de excluir dados do Elasticsearch.


DICA: procure usar índices baseados em tempo para gerenciar a retenção de dados sempre que possível. Agrupe os dados em índices com base no período de retenção. Os índices baseados em tempo também tornam fácil variar o número de réplicas e shards principais com o tempo, uma vez que isso pode ser alterado para o próximo índice a ser gerado. Isso simplifica a adaptação a eventuais alterações feitas em volumes de dados e requisitos.


Os índices e shards não são gratuitos?

Para cada índice do Elasticsearch, as informações sobre mapeamentos e estado são armazenadas no estado do cluster. Elas são mantidas na memória para agilizar o acesso. Assim, ter um grande número de índices e shards em um cluster pode resultar em um estado de cluster grande, principalmente se os mapeamentos forem grandes. Isso pode tornar lento o processo de atualização, porque todas as atualizações precisam ser feitas por meio de um único thread para garantir a consistência antes que as alterações sejam distribuídas no cluster.


DICA: para reduzir o número de índices e evitar mapeamentos grandes e dispersos, avalie armazenar dados com estrutura semelhante no mesmo índice em vez de dividi-los em índices separados com base no local de origem dos dados. É importante encontrar um bom equilíbrio entre o número de índices e shards, e o tamanho do mapeamento para cada índice individual. Como o estado do cluster é carregado no heap em cada nó (incluindo os nós master), e a quantidade de heap é diretamente proporcional ao número de índices, campos por índice e shards, é importante também monitorar a utilização de heap nos nós master e garantir que tenham tamanho apropriado.  


Cada shard tem dados que precisam ser mantidos na memória e usam espaço de heap. Isso inclui estruturas de dados que contêm informações no nível de shard, mas também no nível de segmento para definir onde os dados residem no disco. O tamanho dessas estruturas de dados não é fixo e vai variar dependendo do caso de uso.

Uma característica importante do overhead relacionado ao segmento, entretanto, é que ele não é estritamente proporcional ao tamanho do segmento. Isso significa que segmentos maiores têm menos overhead por volume de dados em comparação com segmentos menores. A diferença pode ser substancial.

Para poder armazenar tantos dados quanto possíveis por nó, torna-se importante gerenciar a utilização de heap e reduzir a quantidade de overhead ao máximo possível. Quanto mais espaço de heap um nó tiver, mais dados e shards ele poderá manipular.

Assim, os índices e shards não são gratuitos do ponto de vista de um cluster, porque há algum nível de overhead de recursos para cada índice e shard.


DICA: pequenos shards resultam em pequenos segmentos, o que aumenta o overhead. Procure manter o tamanho médio do shard entre alguns GB e algumas dezenas de GB. Para casos de uso com dados baseados em tempo, é comum ver shards entre 20 GB e 40 GB de tamanho.

DICA: como o overhead por shard depende do número e do tamanho dos segmentos, obrigar a mesclagem de segmentos menores com maiores por meio de uma operação forcemerge pode reduzir o overhead e melhorar o desempenho das consultas. O ideal é que isso seja feito depois que nenhum outro dado for gravado no índice. Esteja ciente de que essa é uma operação cara que deve ser executada fora do horário de pico.

DICA: o número de shards que você pode manter em um nó será proporcional à quantidade de heap disponível, mas não há limite fixo imposto pelo Elasticsearch. Uma ótima regra prática é garantir que você mantenha o número de shards por nó abaixo de 20 por GB de heap que ele configurou. Portanto, um nó com heap de 30 GB deve ter no máximo 600 shards, mas quanto mais abaixo desse limite você conseguir mantê-lo, melhor será. Isso geralmente ajudará o cluster a se manter com boa integridade. (Nota do editor: a partir da versão 8.3, reduzimos drasticamente o uso de heap por shard, atualizando assim a regra prática descrita neste post. Siga a DICA abaixo para versões 8.3+ do Elasticsearch.)

NOVA DICA: permita 1 KB de heap por campo por índice em nós de dados, mais overheads
O uso exato de recursos de cada campo mapeado depende de seu tipo, mas uma regra prática é permitir aproximadamente 1 KB de overhead de heap por campo mapeado por índice mantido por cada nó de dados. Você também deve permitir heap suficiente para o uso de linha de base do Elasticsearch, bem como sua carga de trabalho, como indexação, buscas e agregações. Um heap extra de 0,5 GB será suficiente para muitas cargas de trabalho razoáveis, e você poderá precisar de menos ainda se sua carga de trabalho for muito leve, enquanto cargas de trabalho pesadas podem exigir mais.

Por exemplo, se um nó de dados contém shards de mil índices, cada um contendo 4 mil campos mapeados, você deve permitir aproximadamente 1.000 × 4.000 × 1 KB = 4 GB de heap para os campos e outros 0,5 GB de heap para sua carga de trabalho e outros overheads; portanto, esse nó precisará de um heap de pelo menos 4,5 GB.


De que maneira o tamanho do shard influencia no desempenho?

No Elasticsearch, cada consulta é executada em um único thread por shard. Entretanto, vários shards podem ser processados paralelamente, assim como várias consultas e agregações com base no mesmo shard.

Isso significa que a mínima latência de consulta, quando não há cache envolvido, dependerá dos dados, do tipo de consulta e do tamanho do shard. A consulta de muitos shards pequenos tornará o processamento por shard mais rápido, mas como muito mais tarefas precisam ser enfileiradas e processadas em sequência, isso não necessariamente será mais rápido do que consultar um número menor de shards maiores. Ter muitos shards pequenos também pode reduzir a taxa de transferência de consulta se houver várias consultas simultâneas.


DICA: a melhor maneira de determinar o tamanho máximo do shard do ponto de vista do desempenho das consultas é adotar um parâmetro de comparação usando dados e consultas realistas. Sempre adote um parâmetro de comparação com uma consulta e um representante de carga de indexação daquilo que o nó precisaria manipular em produção, pois a otimização para uma única consulta pode gerar resultados enganosos.


Como gerencio o tamanho do shard?

Ao usar índices baseados no tempo, cada índice tradicionalmente é associado a um período de tempo fixo. Os índices diários são muito comuns e em geral são usados para manter dados com curto período de retenção ou grandes volumes diários. Isso permite que o período de retenção seja gerenciado com boa granularidade e facilita o ajuste para alterações feitas em volumes diariamente. Dados com um período de retenção mais longo, principalmente se os volumes diários não garantirem o uso de índices diários, em geral usam índices semanais ou mensais para manter o shard grande. Isso reduz o tamanho dos índices e shards que precisam ser armazenados no cluster com o tempo.


DICA: se estiver usando índices baseados no tempo abrangendo um período fixo, ajuste o período que cada índice abrange com base no período de retenção e nos volumes de dados esperados para atingir o tamanho-alvo para o shard.


Índices baseados no tempo com intervalo de tempo fixo funcionam bem quando os volumes de dados são razoavelmente previsíveis e se alteram lentamente. Se a taxa de indexação puder variar rapidamente, ficará muito difícil manter um tamanho-alvo uniforme para o shard.

Para poder lidar melhor com esse tipo de cenário, as APIs de rollover e encolhimento foram introduzidas. Elas adicionam bastante flexibilidade à maneira como os índices e shards são gerenciados, especificamente para índices baseados no tempo.

A API de índice de rollover possibilita especificar o número de documentos que um índice deve conter e/ou o período máximo em que os documentos devem ser gravados nele. Depois que um desses critérios tiver sido excedido, o Elasticsearch poderá disparar um novo índice a ser criado para gravar sem tempo de inatividade. Em vez de precisar que cada índice abranja um período específico, agora é possível mudar para um novo índice com tamanho específico, o que possibilita atingir mais facilmente um tamanho de shard uniforme para todos os índices.

Nos casos em que os dados podem ser atualizados, não há mais uma ligação distinta entre o carimbo de data e hora do evento e o índice no qual ele reside ao usar essa API, o que pode tornar as atualizações significativamente menos eficientes porque cada atualização pode precisar ser precedida de uma busca.


DICA: se você tiver dados imutáveis baseados em tempo em que os volumes podem variar significativamente com o tempo, avalie o uso da API de índice de rollover para atingir um tamanho-alvo para o shard ideal variando dinamicamente o período que cada índice abrange. Isso oferece ótima flexibilidade e pode ajudar a evitar a necessidade de ter shards muito grandes ou muito pequenos quando os volumes são imprevisíveis.


A API de encolhimento de índices permite encolher um índice existente para um novo índice com menos shards principais. Se uma dispersão uniforme de shards entre os nós for desejada durante a indexação, mas isso resultará em shards muito pequenos, essa API poderá ser usada para reduzir o número de shards principais depois que houver a indexação. Isso resultará em shards maiores, mais adequados para armazenamento de dados por períodos mais longos.


DICA: se você precisar que cada índice abranja um período específico, mas ainda quiser poder dispersar a indexação entre um grande número de nós, avalie o uso da API de encolhimento para reduzir o número de shards principais depois que não houver mais indexação. Essa API também pode ser usada para reduzir o número de shards caso você tenha inicialmente configurado muitos shards.


Conclusões

Esta postagem de blog forneceu dicas e orientações práticas sobre como gerenciar melhor os dados no Elasticsearch. Se tiver interesse em saiba mais, a publicação "Elasticsearch: the definitive guide" contém uma seção sobre como projetar para escala, que vale a pena ler apesar de ser um pouco antiga.

Muitas decisões em torno de como distribuir melhor os dados entre índices e shards, entretanto, dependerão de especificidades dos casos de uso, e às vezes poderá ser difícil determinar como aplicar melhor o aviso disponível. Para saber de mais avisos aprofundados e pessoais, participe conosco comercialmente através de uma assinatura e deixe que as equipes de Suporte e Consultoria ajudem a acelerar o projeto. Se quiser discutir seu caso de uso abertamente, você também poderá obter ajuda da nossa comunidade e por meio do nosso fórum público.


Post publicado originalmente em 18 de setembro de 2017 e atualizado em 6 de julho de 2022.