Elasticsearch


Elasticsearch: Conheça a ferramenta certa para fazer buscas!


Objetivos do post:

  • Apresentar a ferramenta Elasticsearch
  • Mostrar como é fácil executá-la em sua máquina para começar a aprender como utilizá-la
  • Utilizar endpoints básicos para obter informações sobre o cluster, nós e índices

O que é?

Logo do Elasticsearch
Figura 1 - Logo do Elasticsearch

Se você tem desenvolvido software nos últimos 5 anos, é grande a chance de você já ter ouvido sobre Elasticsearch. Segundo a Elastic, a empresa responsável pelo seu desenvolvimento, o Elasticsearch é:

O Elasticsearch é um mecanismo de busca e análise de dados distribuído e open source para todos os tipos de dados, incluindo textuais, numéricos, geoespaciais, estruturados e não estruturados.

Vamos quebrar essa frase em pedaços para entendê-la completamente.

Primeiro, uma notícia boa: é um projeto open source, ou seja, pode ser clonado, alterado e utilizado de maneira gratuita (\o/). Seu código está disponível nesse repositório e pode ser acessado por qualquer interessado.

Segundo, é um mecanismo de busca capaz encontrar informações em diversos tipos de dados, como textos, números, datas e até mesmo dados geoespaciais (associados a coordenadas). Ele pode ser considerado uma base de dados NOSQL do tipo "Search Engine". Se você ficou na dúvida, ou quer mais detalhes do que é um Search Engine, recomendo a leitura desse post.

Terceiro, ele é capaz de armazenar e buscar dados estruturados e não estruturados. Isso significa, que não é necessária a definição prévia de um schema a ser seguido pelos dados. Por exemplo, imaginemos que no começo do desenvolvimento, eu armazene apenas três informações sobre cada pessoa cadastrada na minha aplicação:

  • CPF
  • Nome
  • Data de nascimento

Em um banco de dados relacional, por exemplo, eu teria que definir a priori que CPF será um número (nunca faça isso!!!), nome uma string e data de nascimento uma data.

No caso do Elasticsearch, isso não é necessário, pois ele resolve automaticamente o tipo do dado para um tipo conhecido por ele, como por exemplo boleano, ponto flutuante, inteiro, datas, e strings. Além disso, se a partir de um determinado momento eu quiser armazenar mais uma informação, como por exemplo a cidade de nascimento, eu posso simplesmente inserir o dado sem ter que me preocupar em definir o tipo de dado em um schema e sem ter que preencher obrigatoriamente essa informação para os registros antigos.

Isso não significa que você não possa definir previamente um schema. Aliás, sempre que você tiver conhecimento prévio dos dados que serão inseridos, pode (e é desejável) definir, ou atualizar o schema, permitindo assim que sejam realizadas otimizações na busca. Mas novamente, graças a essa flexibilidade do Elasticsearch, você não é obrigado a fazê-lo.

Em geral, o Elasticsearch é utilizado em conjunto com alguma outra base de dados, seja ela relacional, ou NoSQL, sendo utilizado primariamente para busca de dados. Outra utilização bastante comum é utilizá-lo como um centralizador de logs para aplicações que rodam mais de uma instância, como as que rodam no Kubernetes por exemplo, ou para identificar um usuário em aplicações diferentes, usando por exemplo o Elastic Common Schema (ECS)

Outro modo de utilização do Elasticsearch que têm crescido bastante é em conjunto com o Logstash e o Kibana, formando a stack conhecida como ELK (formado pelas iniciais de cada ferramenta). Com essa stack é possível criar gráficos e dashboards, definir alertas, aplicar machine learning e muito mais.

Dependendo do contexto, você ainda pode utilizar o Elasticsearch como sua base de dados principal, no entanto, é recomendável você conhecer e entender suas limitações quando utilizado desse modo.

Características

Até agora só falamos o que é o Elasticsearch, mas ainda não entendemos quais suas características e suas diferenças em relação a outras ferramentas, como bancos de dados relacionais, por exemplo. Vamos então falar sobre suas características:

Altamente escalável

É possível criar um cluster com várias instâncias do Elasticsearch, o que permite que ele rode rapidamente queries em uma quantidade bem grande de dados armazenados. Além disso, se for necessário, você pode adicionar mais nós com o tempo, o que é chamado de escalabilidade horizontal.

Suporte a diversas linguagens de programação

O Elasticsearch fornece suporte a diversas linguagens de programação com Java, Ruby, PHP, Javascript, entre outras.

Além disso, é possível integrá-la facilmente com sua aplicação por meio de suas APIs Rest.

Quase tempo real (near real-time)

Ao utilizar o Elasticsearch, é importante entender que ao inserir um dado no Elasticsearch, ele não aparecerá imediatamente nas consultas subsequentes, pois inicialmente as informações são inseridas em um buffer de indexação na memória, e somente depois são adicionadas a um novo segmento, o qual é escrito no cache do sistema de arquivos. A partir desse momento, o dado já aparece nas buscas, sendo que posteriormente o segmento é efetivamente gravado no disco.

Esse processo de escrever os segmentos no cache do sistema de arquivos é denominado no Elasticsearch como refresh, e por padrão ocorre a cada 1 segundo em índices que tenham recebido ao menos uma requisição de busca nos últimos 30 segundos. Ou seja, em geral, o dado estará pronto para ser pesquisado em 1 segundo ou menos, o que convenhamos é muito rápido.

Para conhecer mais detalhes desse processo, veja esse site.

Armazenamento de estruturas complexas

Em vez de armazenar os dados somente como uma tabela, assim como fazem os bancos de dados relacionais, o Elasticsearch é capaz de armazenar dados complexos que tenham sido serializados como JSON. Essas estruturas de dados complexos são chamadas de documentos, e podem ser tão complexas quanto se queira, conforme exemplo abaixo:

{
    "nome": "Homer Simpson"
    "sexo": "Masculino",
    "dataNascimento": "2001-03-01"
    "endereco": {
        "logradouro": "Rua XV de Novembro"
        "numero": 119922
        "cidade": {
            "id": 421421521521,
            "nome": "Paris",
            "pais": "França"            
        }
    }
}

Ao inserir esse documento em sua base, o Elasticsearch identifica cada uma das palavras presentes no documento, e associa essas palavras a cada um dos documentos da base criando uma estrutura conhecida como índice invertido. Este material dá uma boa ideia de como é realiazado esse processo.

Uma vez criado esse índice, quando você faz uma busca, por exemplo por "Paris", o Elasticsearch não precisa ir passando de documento em documento para descobrir quais documentos apresentam essa palavra, ele simplesmente consulta o índice invertido e retorna os documentos corretos.

Quando mais de uma palavra é buscada, ele repete esse processo para cada palavra, e recupera somente documentos que contenham todas as palavras buscadas (intersecção dos resultados para cada palavra da busca).

Atuando assim, o Elasticsearch consegue responder muito rápido (quase instantaneamente) a buscas nesse conjunto de documentos complexos.

Conceitos

Agora que já vimos algumas características do Elasticsearch, vamos conhecer seus conceitos básicos.

Um nó do Elasticsearch pode ser entendido como uma máquina, seja ela real ou virtual, executando uma instância do Elasticsearch. Essa instância faz parte de um cluster (ainda que o cluster seja composto somente pelo próprio nó) e recebe um nome (por padrão um identificado UUID) durante sua inicialização.

No Elasticsearch um nó pode ter diferentes papéis. Os principais estão listados a seguir. Acesse aqui os demais papéis:

  • Master: Nós que recebem esse papel podem ser eleitos como mestre do cluster (master node). O nó mestre é responsável por:
    • Manter o status do cluster
    • Criar ou remover índices
    • Acompanhar quais nós fazem parte do cluster
    • Decidir em qual nó será alocado um determinado shard
  • Ingest: Nós de ingestão (ingest node). Esses nós possuem como responsabilidade a transformação, e/ou o enriquecimento dos documentos antes de serem inseridos na base de dados. Por exemplo, poderiam transformar o formato da data recebido para um outro formato padrão.
  • Data: Nós de dados (data nodes) armazenam os shards com os documentos. É neles que a diversão acontece. São responsáveis pela adição, remoção, alteração e busca de dados. É importante que esses nós tenham uma boa quantidade de memória disponível, assim como boa capacidade de processamento e de escrita e leitura de disco, para que possam cumprir bem seu papel.

É importante ressaltar que um nó pode ter tanto somente um desses papéis, ou uma combinação deles, inclusive podendo executar todos os papéis disponíveis. Em geral, é recomendável ter no mínimo uma separação entre nós mestre e de dados, para que sobrecargas causadas por consultas, ou manipulação de dados, não atrapalhem tarefas de gerência do cluster.

Cluster

Um cluster do Elasticsearch é um conjunto de nós, que juntos, armazenam e permitem a busca em toda a coleção de documentos armazenados. Um cluster pode ser composto de apenas um nó, ou de vários nós. Obviamente que ao utilizar um conjunto de nós, você tende a aumentar a disponibilidade e a resiliência da solução, pois se uma máquina por exemplo tiver problemas, as demais podem suprir sua falta.

Documento

Como vimos anteriormente um documento é uma estrutura de dados complexa, no formato JSON. Cada documento pode ter uma estrutura diferente dos demais. No entanto, é comum que armazenemos um conjunto de documentos que apresentem estrutura de dados igual, ou muito semelhante aos outros do mesmo conjunto, dando origem a um índice.

Índice

Um índice é um conjunto de documentos que compartilham uma estrutura semelhante. Por exemplo, considerando uma loja, poderíamos ter um índice que armazena documentos com informações pessoais dos clientes, como nome e CPF, e outro índice para armazenar documentos com informações dos produtos, como nome, categoria, fabricante e preço.

Cada índice tem um nome único. Esse nome é utilizado definir em qual índice um documento será inserido, de qual índice será removido, ou em qual índice desejamos realizar a busca.

Shard

Um shard é um subconjunto "buscável" de documentos de um índice. Ou seja, é como se ele fosse também um índice. Ao dividir o índice em shards, o Elasticsearch consegue distribuir os documentos do índice entre as máquinas do cluster. A vantagem de fazer isso é que ao fazer uma consulta em um índice, a consulta pode ser feita de modo simultâneo em cada um dos shards que compõem o índice, tornando ainda mais velozes as buscas.

Réplicas

Imagine a seguinte situação. Você tem um cluster com 3 máquinas (A, B e C) e um índice 10000 documentos. O Elasticsearch faz uma divisão do índice em 3 shards (1, 2 e 3), dois (1 e 2) com 3000 documentos cada, e um (3) com 4000 documentos. O shard 3 com 4000 documentos é atribuído para a máquina A, o shard 2 vai para máquina C e o shard 1 vai para a máquina B. Essa situação é representada na tabela a seguir:

Máquina Shard Primário
A 3
B 1
C 2

Se por algum motivo a máquina A perder contato com o cluster, a busca não conseguirá recuperar nenhum dos 4000 documentos do shard 3, que estavam na máquina A

Para resolver esse problema, o Elasticsearch permite a utilização de réplicas. Vamos supor que determinamos que os shards terão 2 réplicas, shard primário e uma cópia.

Nessa situação, além de fazer a divisão dos shards, conforme exemplo anterior, o Elasticsearch também faz uma cópia de cada shard e distribui para cada um dos nós. Por exemplo, além do shard 3, a máquina A receberia uma cópia do shard 1. A máquina B, receberia o shard 1 e uma cópia do shard 2, e a máquina C receberia o shard 2, e uma cópia do shard 3. Essa situação é apresentada na tabela a seguir:

Máquina Shard Primário Réplica
A 3 1
B 1 2
C 2 3

Com as três máquinas ativas, a consulta no shard 3 é executada pela máquina A, no shard 1 pela máquina B e no shard 2 pela máquina C. Caso a máquina A caia, a máquina C realiza consulta no shard 3, pois possui uma réplica desse shard, além de buscar também no shard 2. Desse modo, mesmo com a queda da máquina A, ainda é possível realizar a consulta em todos os documentos armazenados.

Instalação

Para fazer o download da imagem do Elasticsearch, vamos utilizar o comando docker pull, do seguinte modo:

docker pull docker.elastic.co/elasticsearch/elasticsearch-oss:7.8.1

Cuja saída será semelhante a essa:

7.8.1: Pulling from elasticsearch/elasticsearch-oss
86dbb57a3083: Pull complete 
5fdf77474473: Pull complete 
61d17dd06b58: Pull complete 
360d30f1f392: Pull complete 
af683f366b81: Pull complete 
896bf14b5823: Pull complete 
2a33fe5725cc: Pull complete 
f65c88d9f219: Pull complete 
bcd9971fefce: Pull complete 
Digest: sha256:b004e1138653a919216b0ac33d27fb6650dc6b2a0873c46c4a3a8391ffc7f04e
Status: Downloaded newer image for docker.elastic.co/elasticsearch/elasticsearch-oss:7.8.1
docker.elastic.co/elasticsearch/elasticsearch-oss:7.8.1

Podemos conferir o tamanho da imagem com o comando a seguir:

docker images

A saída nos mostra que a imagem tem quase 700MB:

REPOSITORY                                          TAG                 IMAGE ID            CREATED             SIZE
docker.elastic.co/elasticsearch/elasticsearch-oss   7.8.1               e443f312d43d        13 days ago         698MB

Inicializando a instância

Podemos agora iniciar uma instância do Elasticsearch com o comando docker run

docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -d --name elasticsearch docker.elastic.co/elasticsearch/elasticsearch-oss:7.8.1

Nele estamos mapeando as portas 9200 e 9300 da nossa máquina, respectivamente para as portas 9200 e 9300 do container. Além disso, estamos definindo a variável de ambiente discovery.type com o valor single-node, uma vez que vamos executar somente uma instância do Elasticseach, e não um cluster. O parâmetro -d informa ao Docker que queremos rodar o container em background, sem que o docker bloqueei nosso terminal. Já o parâmetro --name define o nome do container como elasticsearch. O último parâmetro do comando é a imagem que acabamos de baixar.

Para conferirmos que o container está rodando, podemos executar o comando docker ps:

docker ps
CONTAINER ID        IMAGE                                                     COMMAND                  CREATED             STATUS              PORTS                                            NAMES 
5349c3ce7e56        docker.elastic.co/elasticsearch/elasticsearch-oss:7.8.1   "/tini -- /usr/local…"   2 seconds ago       Up 1 second         0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp   elasticsearch

Esse comando nos mostra há quanto tempo foi criado o comando (2 segundos atrás), há quanto tempo ele está realmente funcionando (1 segundo atrás), bem como o mapeamento das portas do nosso computador para as portas do container.

Utilização das APIs

Vamos agora realizar algumas chamadas da API do Elasticsearch para nos familiarizarmos com ela. A API é disponibilizada pelo Elasticsearch na porta 9200. Por meio dela conseguimos obter informações sobre o cluster, os nós, índices, bem como inserir, remover, atualizar ou buscar documentos. Vamos utilizar o curl para acessar a API. Nosso foco nesse post, serão as APIs utilizadas para obter informações sobre o cluster, os nós e os índices. Nos demais posts, vamos utilizar APIs para inserção, remoção, alteração e busca de dados.

Vejamos agora alguns endpoints interessantes. É importante ressaltar que todos os que serão apresentados aqui são chamados por meio de um método http GET:

/ (root)

O primeiro endpoint que vamos utilizar é o /:

curl localhost:9200/

Cujo retorno é um JSON com informações básicas sobre o cluster, como por exemplo nome (docker-cluster), uuid (RI9ELMk7TxOeij5BuhOamg), bem como informações sobre a versão utilizada (7.8.1).

{
  "name" : "5349c3ce7e56",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "RI9ELMk7TxOeij5BuhOamg",
  "version" : {
    "number" : "7.8.1",
    "build_flavor" : "oss",
    "build_type" : "docker",
    "build_hash" : "b5ca9c58fb664ca8bf9e4057fc229b3396bf3a89",
    "build_date" : "2020-07-21T16:40:44.668009Z",
    "build_snapshot" : false,
    "lucene_version" : "8.5.1",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

/_cat

Outro endpoint interessante é o /_cat, com um método GET. Ele retorna diversos endpoints que podemos utilizar para obter informações sobre os nós do cluster (/_cat/nodes), a lista de índices (/_cat/indices), consumo de disco e alocação de shards (_cat/allocation) e saúde do cluster (/_cat/health).

Todos os endpoints iniciados com /_cat/ são voltados para a consulta de humanos, apresentando um formato mais fácil de ser lido do que um JSON por exemplo. Todos eles aceitam o parâmetro v, que aumenta a verbosidade do comando, retornando o cabecalho das tabelas, e o parâmetro help, que apresenta uma descrição do que significa cada coluna. O parâmetro help é interessante, pois retorna não somente as colunas que são retornadas de maneira padrão por cada comando, mas também outras colunas que podem ser adicionadas. Vamos executar alguns endpoints do /_cat/ e ver como usar o parâmetro help e como selecionar as colunas que queremos exibir:

/_cat/nodes

Como mencionado anteriormente, o endpoint /_cat/nodes retorna informações sobre os nós presentes no cluster. No nosso caso, temos somente um nó no cluster. Vamos chamar esse endpoint com o parâmetro v (de verboso), que retorna o nome de cada coluna apresentada:

curl "localhost:9200/_cat/nodes?v"

O retorno foi o seguinte:

ip         heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
172.26.0.2           18          97  18    1.07    1.54     1.92 dimr      *      5349c3ce7e56

Observe que a primeira coluna informa o IP da instância, atribuído pelo Docker, o qual podemos confirmar com o comando a seguir:

docker inspect elasticsearch -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'

A segunda coluna (heap.percent) informa a porcentagem do heap utilizada pelo nó, a terceira (ram.percent) informa a porcentagem de memória RAM, já a quarta (cpu) a porcentagem de CPU utilizada, e as próximas três colunas (load_1m, load_5m, load_15m) informa a média de processos requisitando a utilização de CPU respectivamente no último minuto, nos últimos 5 minutos e nos últimos 15 minutos.

A coluna node.role indica os papéis assumidos pelo nó, no nosso caso temos as letras:

  • d: nó de dados, ou seja, os dados são salvos nesse nó
  • i: nó de ingestão, significando que são nós responsáveis por realizar um pré-processamento (transformação) das informações recebidas antes de inserí-las na base.
  • m: nó elegível a mestre do cluster
  • r: nó cliente de cluster remoto, ou seja, podemos acessar a API por meio dele.

A coluna master nos mostra que esse nó atualmente é o mestre do cluster. E por último, temos o nome do nó, que por rodarmos a imagem Docker, temos como padrão que o nome do nó é igual ao id do container Docker, como podemos verificar no comando docker ps.

Vamos agora executar esse endpoint com o parâmetro help para obtermos uma lista com todas as colunas que podemos exibir e sua descrição:

curl "localhost:9200/_cat/nodes?help"
id                                 | id,nodeId                                   | unique node id                                                                                                   
pid                                | p                                           | process id                                                                                                       
ip                                 | i                                           | ip address                                                                                                       
port                               | po                                          | bound transport port                                                                                             
http_address                       | http                                        | bound http address                                                                                               
version                            | v                                           | es version                                                                                                       
flavor                             | f                                           | es distribution flavor                                                                                           
type                               | t                                           | es distribution type                                                                                             
build                              | b                                           | es build hash                                                                                                    
jdk                                | j                                           | jdk version                                                                                                      
disk.total                         | dt,diskTotal                                | total disk space                                                                                                 
disk.used                          | du,diskUsed                                 | used disk space                                                                                                  
disk.avail                         | d,da,disk,diskAvail                         | available disk space                                                                                             
disk.used_percent                  | dup,diskUsedPercent                         | used disk space percentage                                                                                       
heap.current                       | hc,heapCurrent                              | used heap                                                                                                        
heap.percent                       | hp,heapPercent                              | used heap ratio                                                                                                  
heap.max                           | hm,heapMax                                  | max configured heap                                                                                              
ram.current                        | rc,ramCurrent                               | used machine memory                                                                                              
ram.percent                        | rp,ramPercent                               | used machine memory ratio                                                                                        
ram.max                            | rm,ramMax                                   | total machine memory                                                                                             
file_desc.current                  | fdc,fileDescriptorCurrent                   | used file descriptors                                                                                            
file_desc.percent                  | fdp,fileDescriptorPercent                   | used file descriptor ratio                                                                                       
file_desc.max                      | fdm,fileDescriptorMax                       | max file descriptors                                                                                             
cpu                                | cpu                                         | recent cpu usage                                                                                                 
load_1m                            | l                                           | 1m load avg                                                                                                      
load_5m                            | l                                           | 5m load avg                                                                                                      
load_15m                           | l                                           | 15m load avg                                                                                                     
uptime                             | u                                           | node uptime                                                                                                      
node.role                          | r,role,nodeRole                             | m:master eligible node, d:data node, i:ingest node, -:coordinating node only                                     
master                             | m                                           | *:current master                                                                                                 
name                               | n                                           | node name                                                                                                        
completion.size                    | cs,completionSize                           | size of completion                                                                                               
fielddata.memory_size              | fm,fielddataMemory                          | used fielddata cache                                                                                             
fielddata.evictions                | fe,fielddataEvictions                       | fielddata evictions                                                                                              
query_cache.memory_size            | qcm,queryCacheMemory                        | used query cache                                                                                                 
query_cache.evictions              | qce,queryCacheEvictions                     | query cache evictions                                                                                            
request_cache.memory_size          | rcm,requestCacheMemory                      | used request cache                                                                                               
request_cache.evictions            | rce,requestCacheEvictions                   | request cache evictions                                                                                          
request_cache.hit_count            | rchc,requestCacheHitCount                   | request cache hit counts                                                                                         
request_cache.miss_count           | rcmc,requestCacheMissCount                  | request cache miss counts                                                                                        
flush.total                        | ft,flushTotal                               | number of flushes                                                                                                
flush.total_time                   | ftt,flushTotalTime                          | time spent in flush                                                                                              
get.current                        | gc,getCurrent                               | number of current get ops                                                                                        
get.time                           | gti,getTime                                 | time spent in get                                                                                                
get.total                          | gto,getTotal                                | number of get ops                                                                                                
get.exists_time                    | geti,getExistsTime                          | time spent in successful gets                                                                                    
get.exists_total                   | geto,getExistsTotal                         | number of successful gets                                                                                        
get.missing_time                   | gmti,getMissingTime                         | time spent in failed gets                                                                                        
get.missing_total                  | gmto,getMissingTotal                        | number of failed gets                                                                                            
indexing.delete_current            | idc,indexingDeleteCurrent                   | number of current deletions                                                                                      
indexing.delete_time               | idti,indexingDeleteTime                     | time spent in deletions                                                                                          
indexing.delete_total              | idto,indexingDeleteTotal                    | number of delete ops                                                                                             
indexing.index_current             | iic,indexingIndexCurrent                    | number of current indexing ops                                                                                   
indexing.index_time                | iiti,indexingIndexTime                      | time spent in indexing                                                                                           
indexing.index_total               | iito,indexingIndexTotal                     | number of indexing ops                                                                                           
indexing.index_failed              | iif,indexingIndexFailed                     | number of failed indexing ops                                                                                    
merges.current                     | mc,mergesCurrent                            | number of current merges                                                                                         
merges.current_docs                | mcd,mergesCurrentDocs                       | number of current merging docs                                                                                   
merges.current_size                | mcs,mergesCurrentSize                       | size of current merges                                                                                           
merges.total                       | mt,mergesTotal                              | number of completed merge ops                                                                                    
merges.total_docs                  | mtd,mergesTotalDocs                         | docs merged                                                                                                      
merges.total_size                  | mts,mergesTotalSize                         | size merged                                                                                                      
merges.total_time                  | mtt,mergesTotalTime                         | time spent in merges                                                                                             
refresh.total                      | rto,refreshTotal                            | total refreshes                                                                                                  
refresh.time                       | rti,refreshTime                             | time spent in refreshes                                                                                          
refresh.external_total             | rto,refreshTotal                            | total external refreshes                                                                                         
refresh.external_time              | rti,refreshTime                             | time spent in external refreshes                                                                                 
refresh.listeners                  | rli,refreshListeners                        | number of pending refresh listeners                                                                              
script.compilations                | scrcc,scriptCompilations                    | script compilations                                                                                              
script.cache_evictions             | scrce,scriptCacheEvictions                  | script cache evictions                                                                                           
script.compilation_limit_triggered | scrclt,scriptCacheCompilationLimitTriggered | script cache compilation limit triggered                                                                         
search.fetch_current               | sfc,searchFetchCurrent                      | current fetch phase ops                                                                                          
search.fetch_time                  | sfti,searchFetchTime                        | time spent in fetch phase                                                                                        
search.fetch_total                 | sfto,searchFetchTotal                       | total fetch ops                                                                                                  
search.open_contexts               | so,searchOpenContexts                       | open search contexts                                                                                             
search.query_current               | sqc,searchQueryCurrent                      | current query phase ops                                                                                          
search.query_time                  | sqti,searchQueryTime                        | time spent in query phase                                                                                        
search.query_total                 | sqto,searchQueryTotal                       | total query phase ops                                                                                            
search.scroll_current              | scc,searchScrollCurrent                     | open scroll contexts                                                                                             
search.scroll_time                 | scti,searchScrollTime                       | time scroll contexts held open                                                                                   
search.scroll_total                | scto,searchScrollTotal                      | completed scroll contexts                                                                                        
segments.count                     | sc,segmentsCount                            | number of segments                                                                                               
segments.memory                    | sm,segmentsMemory                           | memory used by segments                                                                                          
segments.index_writer_memory       | siwm,segmentsIndexWriterMemory              | memory used by index writer                                                                                      
segments.version_map_memory        | svmm,segmentsVersionMapMemory               | memory used by version map                                                                                       
segments.fixed_bitset_memory       | sfbm,fixedBitsetMemory                      | memory used by fixed bit sets for nested object field types and type filters for types referred in _parent fields
suggest.current                    | suc,suggestCurrent                          | number of current suggest ops                                                                                    
suggest.time                       | suti,suggestTime                            | time spend in suggest                                                                                            
suggest.total                      | suto,suggestTotal                           | number of suggest ops

Vamos supor agora que queremos mostrar somente o ip, e uptime (tempo que a instância está sendo executada). Para isso, vamos utilizar o parâmetro h (de header, que significa cabeçalho):

curl "localhost:9200/_cat/nodes?v&h=ip,uptime"
ip         uptime
172.26.0.2   5.6h

Nesse comando, usamos o nome completo da coluna (primeira coluna do retorno do comando help), mas também poderíamos utilizar a segunda coluna, que é um alias:

curl "localhost:9200/_cat/nodes?v&h=i,u"
i               u
172.26.0.2   5.6h

Note que agora no cabeçalho também é exibido o alias, em vez do nome completo da coluna. Conforme mencionado anteriormente, essa possibilidade de selecionar as colunas que farão parte do retorno, e o parâmetro help, para listar todas as colunas disponíveis podem ser utilizados em qualquer comando /_cat/

/_cat/indices

O endpoint _cat/indices trás informações sobre os índices presentes no cluster. Se você executá-lo agora, terá como resposta somente os nomes das colunas. Por isso apresento um exemplo fictício de informações que esse comando pode retornar:

curl "localhost:9200/_cat/nodes?v"
health status index                                   pri rep docs.count docs.deleted store.size pri.store.size 
green  open   br.com.blogdobruno.pessoa                 5   1       9049          811      5.3mb          5.3mb

A coluna health apresenta a classificação da saúde do índice. Ela pode ser green (quando está tudo certo com o índice, ou seja, quando tanto o(s) shard(s) primário(s), quanto as réplicas estão alocados com sucesso), yellow (quando o(s) shard(s) primário(s) está(ão) alocado(s), mas alguma(s) réplica(s) não está(ão)) e red (quando um ou mais shards primários não estão alocados, ou seja, uma parte dos dados salvos não índice não estão disponíveis).

No nosso exemplo, o índice br.com.blogdobruno.pessoa está com todos os shards alocados corretamente, e portanto, aparece como green.

A coluna status mostra se o índice está open (permitindo leitura e escrita de informações nele), ou closed (operações de escrita e leitura como inserção, remoção, alteração ou busca de dados estão proibidas).

Já a coluna pri mostra o número de shards primários, no nosso caso 5, e a coluna rep mostra o número de réplicas existentes para cada um dos shards (1).

docs.count e docs.deleted indicam, respectivamente, o número de documentos existentes e removidos do índice.

A coluna pri.store.size representa o tamanho em disco ocupado pelos shards primários (5.3MB), já a store.size informa o tamanho total ocupado pelos shards e suas réplicas. Observe que só existe uma réplica de cada shard no nosso caso, portanto esse valor é igual. Se aumentássemos para duas réplicas para cada shard, o valor apresentado na coluna store.size seria o dobro.

/_cat/allocation

Esse endpoint mostra informações sobre a alocação dos shards, bem como utilização de espaço de armazenamento em cada um dos nós do cluster (uma linha para cada nó).

Vamos ver o retorno obtido:

curl "localhost:9200/_cat/allocation?v"
shards disk.indices disk.used disk.avail disk.total disk.percent host       ip         node
     0           0b   150.5gb     54.6gb    205.2gb           73 172.26.0.2 172.26.0.2 5349c3ce7e56

Como o cluster foi recém criado, não temos nenhum shard alocado (0), uma vez que não criamos nenhum índice. A coluna disc.indices exibe a quantidade de espaço ocupada pelos índices do Elasticsearch, já a coluna disk.used mostra o espaço total ocupado no disco, incluindo por exemplo os arquivos do sistema operacional.

disk.avail mostra a quantidade de espaço disponível no disco, e disk.total representa a capacidade total do disco. A coluna disk.percent mostra a porcentagem do disco ocupada. host, ip e node mostram as respectivas informações do nó.

/_cat/health

Por último, mas não menos importante, temos o endpoint /_cat/health que nos dá informações sobre a saúde do cluster:

curl "localhost:9200/_cat/health?v"
epoch      timestamp cluster        status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1596551538 14:32:18  docker-cluster green           1         1      0   0    0    0        0             0                  -                100.0%

timestamp exibe a hora atual do sistema onde está rodando a instância do elasticsearch em formato legível para humanos, já epoch retorna a mesma informação no formato "Unix Time", status mostra o status do cluster, considerando os índices existentes (olhar _cat/indices), node.total é o número total de nós no cluster, node.data representa os nós de dados do cluster, shards e pri informam, respectivamente, o número total de shards (considerando também as réplicas) e o número de somente shards primários,

A coluna relo indica o número de nós sendo realocados, já init indica o número de nós em inicialização. Por sua vez, unassign exibe a quantidade de shards que ainda não foram alocados, pending_tasks o número de tarefas pendentes, max_task_wait_time o tempo de espera mais longo das tarefas pendentes, e active_shards_percent representa a porcentagem de shards ativos alocados.

Resumo da ópera

Nesse post vimos um pouquinho do que é o Elasticsearch, seus conceitos principais, como utilizá-lo localmente, e algumas APIs que podem ser utilizadas para analisar analisar diversos aspectos dos índices, dos nós e do cluster. Mas eu sei que você quer muito mais! Agora você quer efetivamente criar índices, inserir documentos, fazer buscas e liberar todo o poder do Elasticsearch!

Mas calma, isso será feitos nos próximos posts! Continue acompanhando o blog por meio do RSS (lá no topo da página na direita!) para saber quando saem posts novos.

Espero que tenham gostado e até a próxima pessoal!


results matching ""

    No results matching ""