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 é?
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.
Nó
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 clusterr
: 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!