Usos do Elasticsearch e coisas a aprender
ATUALIZAÇÃO: este artigo se refere à nossa oferta hospedada do Elasticsearch com um nome mais antigo: Found. Observe que o Found agora é conhecido como Elastic Cloud.
O Elasticsearch é usado para vários casos de uso diferentes: busca de texto completo “clássica”, armazenamento de analítica, preenchimento automático, verificador ortográfico, mecanismo de alerta e armazenamento de documentos de uso geral. Este artigo fornece uma breve visão geral dos diferentes usos comuns e coisas importantes a serem consideradas, com indicações de onde você pode aprender mais sobre eles.
Introdução
No Found, vemos muitos casos de uso do Elasticsearch diferentes. Muitas vezes nos perguntam: “Qual é o seu cliente típico?”. No entanto, não há uma resposta clara além de “Bem, eles preferem usar seu tempo para construir coisas do que operar um monte de clusters!”. Vemos o Elasticsearch sendo usado de muitas formas incríveis e diferentes, e algumas bem doidas também!
O Elasticsearch ainda é bastante jovem, e nossos clientes tendem a começar com o Elasticsearch para um determinado projeto e, posteriormente, acrescentar mais clusters para logging e analítica.
Uma evolução de desenvolvimento comum começa com a criação de uma busca simples para um site ou uma coleção de documentos. Então, talvez a navegação facetada seja adicionada, bem como verificação ortográfica ou respostas do tipo “você quis dizer...?”. Talvez a busca difusa seja garantida, além do preenchimento automático e possivelmente até “busca enquanto você digita”. Como a relevância é importante, esquemas de classificação mais avançados provavelmente serão adicionados em algum momento — possivelmente com base em quem é o usuário, onde ele está ou quem ele conhece. E, claro, para saber o que os usuários realmente fazem, o uso deve ser registrado em log e as métricas devem ser armazenadas, para que saibamos que tudo funciona bem.
Você pode usar o Elasticsearch para tudo isso e muito mais, mas os diferentes usos vêm com níveis muito diferentes de complexidade e requisitos de recursos.
Para buscar, claro! (Cada vez mais!)
Não é nenhuma surpresa que o Elasticsearch é frequentemente usado para implementar “busca”, geralmente significando que há uma caixa de digitação acompanhada por um ícone de lupa. O que queremos dizer com “busca” pode ser ambíguo neste caso, então vou me referir a diferentes tipos de buscas como, por exemplo, “busca simples”, “busca difusa”, “agregação”, sendo que simples quer dizer o que você pode conseguir com uma consulta match básica.
Muitas pessoas se espantam quando dizemos que a busca simples está entre as tarefas que você pode solicitar ao Elasticsearch que consomem menos recursos. Se tudo o que precisa são os dez melhores resultados de uma consulta match regular e não difusa, você pode realizar centenas de buscas por segundo em coleções de dezenas de milhões de documentos armazenados em hardware barato. No entanto, quando você adiciona busca difusa ou navegação facetada à lista de requisitos, as necessidades de CPU e memória aumentam muito.
Em geral, espera-se que as interfaces de busca modernas tenham algum tipo de navegação facetada, ou seja, onde o usuário possa entender rapidamente a distribuição dos resultados da busca. Quantos livros são de um determinado autor, em uma determinada faixa de preço, com uma determinada classificação? Esses resultados são implementados usando agregações no Elasticsearch e vêm em várias formas. Você pode agregar por termos, intervalos numéricos, intervalos de datas, distância geográfica e muito mais.
É contraintuitivo para muitos que vasculhar milhões de documentos para encontrar correspondências seja de alguma forma menos trabalhoso do que contar e agregar as correspondências de várias maneiras. No entanto, em comparação com o problema de recuperação de informações “Quais dez documentos correspondem a estas condições (e são mais relevantes para elas)?”, a agregação é cara. Ao pontuar para encontrar os melhores documentos, o Lucene usará truques como “Este conjunto de documentos não corresponde a tudo a que estes outros documentos correspondem, portanto, eles não podem ser os melhores, então simplesmente ignore-os”. Ao filtrar, o Elasticsearch utiliza muito o cache do filtro. O Elasticsearch e o Lucene são ótimos para evitar o trabalho quando possível, mas com as agregações, eles precisam contar todas as coisas correspondentes o tempo todo.
No artigo Elasticsearch from the Bottom Up (Elasticsearch de baixo para cima), falamos sobre como o índice invertido funciona e como o dicionário e as listas de publicação são usados para realizar uma busca simples. Esse e nossos artigos sobre análise de texto mostram com clareza por que é muito importante processar o texto corretamente ao trabalhar com busca. Nos artigos Sizing Elasticsearch (Dimensionamento do Elasticsearch) e Elasticsearch in Production (Elasticsearch em produção), detalhamos que tipo de uso de memória você pode esperar.
Analítica
As cargas de trabalho analíticas tendem a contar itens e resumir seus dados — muitos dados, pode até ser big data, o que quer que isso signifique! Elas dependem das agregações do Elasticsearch, e as agregações costumam ser geradas por ferramentas como o Kibana.
Já mencionamos que essas agregações podem ser bastante caras, tanto em CPU quanto em memória. As demandas de memória são grandes, pois o Elasticsearch precisa procurar rapidamente um valor dado a um documento, o que envolve o carregamento de todos os dados de todos os documentos na memória em um “cache de campo”. Isso pode ser amenizado usando “valores de documento”, que precisam ser habilitados no seu mapeamento antes de você indexar documentos.
Além disso, as buscas analíticas geralmente são executadas em dados com carimbo de data/hora, que pode fazer sentido dividir em, por exemplo, índices diários ou mensais. Ter um único índice por unidade de tempo facilita a redução do espaço de busca e a limpeza e o arquivamento de dados antigos.
Busca difusa
Uma busca difusa é aquela que é tolerante com erros de ortografia. Para dar um exemplo, você pode encontrar Levenshtein ao procurar Levenstein. Nosso artigo sobre buscas difusas apresenta mais detalhes sobre o uso de buscas difusas e como elas funcionam.
As buscas difusas são simples de habilitar e podem melhorar muito o “recall”, mas também podem ser muito caras de executar. Por padrão, um termo na entrada pode ser reescrito como um OR de 50 termos por campo, que, combinado com multi_field, pode causar uma explosão combinatória de termos na consulta reescrita resultante.
É sempre importante testar alterações e melhorias em suas buscas com quantidades realistas de dados antes de enviá-las para produção. Isso é particularmente verdadeiro ao adicionar o parâmetro fuzziness. É uma opção fácil de habilitar, mas tornará suas buscas muito mais caras.
As buscas difusas exigem muito da CPU. Adicione-as com cuidado e provavelmente não a todos os campos.
Preenchimento automático e busca instantânea
A busca enquanto o usuário digita ocorre de várias formas. Podem ser sugestões simples de tags existentes, tentar prever uma busca com base no histórico ou apenas fazer uma busca completamente nova para cada pressionamento de tecla (regulado).
Existem muitas funcionalidades diferentes no Elasticsearch para auxiliar na criação desses recursos, como consultas prefix, match_phrase_prefix, ngrams de indexação e uma família de sugestores diferentes.
Buscas como essa são muito sensíveis a latências. Geralmente, considera-se como 100 milissegundos o limite do que não parece mais “instantâneo”. A busca de quase todos os pressionamentos de tecla também significa uma taxa de transferência de busca bem mais alta. Assim, é fundamental que as buscas sejam baratas e que esses índices caibam na memória.
Ao mesmo tempo em que o preenchimento automático das buscas mostra os resultados para a busca concluída mais provável (assim como o Google faz), deve ser considerado como dois problemas de busca separados. A quantidade de dados a serem buscados ao preencher automaticamente as buscas anteriores é provavelmente muito menor do que o conteúdo que está sendo buscado, o que torna mais viável manter tudo na memória e atender a buscas difusas. Como uma busca de preenchimento automático terá uma carga de busca muito maior do que a busca completa, manter os dois separados também torna possível redimensioná-los separadamente, possivelmente em clusters do Elasticsearch completamente separados.
Quando o Soundcloud reformulou sua experiência de busca, eles trabalharam muito nas sugestões. Ao implementá-la bem, eles não apenas observaram um aumento na precisão da busca, mas também uma notável redução na carga da infraestrutura que alimenta a busca completa. O que as pessoas buscam geralmente segue uma distribuição Zipf: normalmente, 10% das buscas únicas representam 90% do volume de busca. Assim, é muito provável que os resultados completos da melhor sugestão de busca já estejam armazenados em cache (na camada da sua aplicação) e possam ser exibidos “instantaneamente”.
Grande parte da engenharia por trás do sugestor de busca do Soundcloud é o que leva aos recursos de sugestão do Elasticsearch. Há uma excelente apresentação de Muir e Willauer sobre sugestões de consulta com o Lucene que vale a pena assistir para saber mais.
Multitenancy
É comum que você tenha vários clientes ou usuários com coleções separadas de documentos, e um usuário nunca deva conseguir buscar documentos que não pertençam a ele. Isso geralmente leva a um design no qual cada usuário tem seu próprio índice.
Na maioria das vezes, isso gera uma quantidade excessiva de índices. Em quase em todos os casos em que vemos uma implementação de um índice por usuário, seria melhor ter um único índice do Elasticsearch maior. Ter um grande número de pequenos índices apresenta desvantagens significativas:
- A sobrecarga de memória não é desprezível. Milhares de pequenos índices consumirão muito espaço de heap. O número de descritores de arquivo também pode explodir.
- Pode haver muita duplicação. Considere como o índice invertido funciona e como o Lucene grava e compacta itens em segmentos.
- Snapshot/restauração é atualmente um processo serial, com uma sobrecarga por índice. Fazer snapshots de milhares de índices minúsculos leva muito mais tempo do que fazer snapshots de alguns índices grandes.
No artigo Sizing Elasticsearch (Dimensionamento do Elasticsearch), há mais informações sobre estratégias para criação de shards e particionamento, com mais algumas referências. Corrigir uma aplicação com um design de índice abaixo do ideal pode exigir um esforço significativo, portanto, vale a pena entender as diferentes abordagens.
Provavelmente não seria recomendável criar um índice por usuário para sua aplicação multitenant.
Sem esquema/esquemas definidos pelo usuário
Ainda com relação a ter vários clientes individuais, também vemos muitos casos de uso em que diferentes usuários podem ter documentos completamente diferentes. Por exemplo, se você estiver fornecendo pesquisas/questionários para o usuário como um serviço, é provável que diferentes pesquisas tenham campos completamente diferentes.
Frequentemente, isso leva ao uso do “mapeamento dinâmico” do Elasticsearch, o que, às vezes, é anunciado como o fato de o Elasticsearch ser sem esquema. No entanto, o Elasticsearch criará um mapeamento para você nos bastidores e pode ser problemático quando ele cresce muito, levando a um “explosão de mapeamento”. Em vez disso, é importante garantir que os valores em um documento também terminem como valores — e não como campos separados. Isso é explicado um pouco mais em “Key/Value Woes” (Problemas de chave/valor).
O Elasticsearch tem funcionalidades versáteis de mapeamento, com modelos de índice, modelos dinâmicos, campos múltiplos e muitas outras. Use-as!
Mesmo quando não estiver usando um mapeamento, saiba qual mapeamento o Elasticsearch cria para você.
Buscas definidas pelo usuário
Com relação aos esquemas definidos pelo usuário, geralmente há a necessidade de permitir que os usuários finais definam suas próprias buscas, com filtros customizados, pontuação e agregações. Uma abordagem comum é limitar a solicitação de busca a determinados índices e/ou isolar as consultas dos usuários com filtros.
Mesmo ao fazer isso, há várias maneiras de um usuário causar estragos quando solicitações de busca customizadas podem ser definidas, como expressar buscas que consomem muita CPU, sobrecarregam a memória ou causam falhas no Elasticsearch. Esses tópicos são abordados em Six Ways to Crash Elasticsearch (Seis maneiras de fazer o Elasticsearch travar) e Securing Your Elasticsearch Cluster (Como proteger seu cluster do Elasticsearch).
Tenha cuidado com as solicitações de busca definidas pelo usuário.
Rastreamento e processamento de documentos
Há muitas maneiras de colocar seus dados no Elasticsearch.
O rio é um conceito do Elasticsearch no qual o Elasticsearch extrai dados de uma fonte, como um banco de dados por meio de JDBC, uma fila de mensagens, um fluxo do Twitter ou o rastreamento de sites. É bastante simples de começar, mas a abordagem rapidamente se mostra desafiadora no redimensionamento e na operação em produção. Como tal, os rios são obsoletos e deve-se procurar resolver esses problemas fora do Elasticsearch. O Logstash continua ganhando suporte para mais sistemas e pode substituir muitos rios. Para aplicações customizadas, há desafios suficientes na sincronização de dados com o Elasticsearch e no preparo de documentos do Elasticsearch para os quais algo simples e genérico como os rios não deve ser suficiente. Para o rastreamento, as pessoas estão usando o Scrapy e o Nutch juntamente com o Elasticsearch.
Relacionado a isso está o processamento e a conversão de documentos como documentos do Word ou PDFs em texto simples que o Elasticsearch pode indexar. Existe um plugin “mapper-attachments” que pode ser usado para fazer essa conversão no Elasticsearch. No entanto, embora o plugin de anexos seja conveniente, recomendamos fazer a conversão dos documentos antes de enviá-los para o Elasticsearch. Isso proporciona o maior nível de controle de como os documentos são convertidos e refinados. Uma conversão de documentos como essa é normalmente uma das primeiras etapas durante o “pipeline de processamento de documentos/textos” do “refinamento de conteúdo”. Os documentos que você envia para o Elasticsearch devem ser o resultado desse “refinamento/preparação de conteúdo”, deixando o Elasticsearch fazer o processamento final do texto e a indexação. A conversão de documentos consome muitos recursos de CPU, mas é facilmente paralelizável. É preferível deixar que o Elasticsearch gaste seu tempo indexando e buscando, e deixar que os clientes de “upstream” façam a conversão do documento.
Envie os dados processados para o Elasticsearch, não extraia e processe dentro do Elasticsearch.
Resumo
Há muito a aprender com o Elasticsearch e, às vezes, pode ser difícil saber o que você precisa aprender.
Neste artigo, abordamos alguns casos de uso comuns e algumas coisas importantes a serem observadas em todos eles. Espero que você tenha encontrado algo novo para aprender que seja relevante para suas necessidades e esteja mais perto de colocar sua aplicação do Elasticsearch em produção.