Otimização de carga em um projeto Highload com ElasticSearch

Olá Habr! Meu nome é Maxim Vasiliev, trabalho como analista e gerente de projetos na FINCH. Hoje, gostaria de contar como, com o ElasticSearch, conseguimos processar 15 milhões de consultas em 6 minutos e otimizar a carga diária no site de um de nossos clientes. Infelizmente, teremos que ficar sem nomes, já que temos a NDA, esperamos que o conteúdo do artigo não seja afetado. Vamos lá.

Como o projeto é organizado


Em nosso back-end, criamos serviços que garantem o desempenho de sites e aplicativos móveis de nosso cliente. A estrutura geral pode ser vista no diagrama:

imagem

No processo, processamos um grande número de transações: compras, pagamentos, operações com saldos de usuários, nas quais armazenamos muitos logs e também importamos e exportamos esses dados para sistemas externos.

Também existem processos reversos quando recebemos dados de um cliente e os transmitimos ao usuário. Além disso, ainda existem processos para trabalhar com pagamentos e programas de bônus.

Fundo curto


Inicialmente, usamos o PostgreSQL como o único data warehouse. Suas vantagens padrão para o SGBD: disponibilidade de transações, linguagem desenvolvida para amostragem de dados, ferramentas amplas para integração; juntamente com o bom desempenho, as pessoas satisfeitas há muito satisfazem nossas necessidades.

Armazenamos absolutamente todos os dados no Postgres: de transações a notícias. Mas o número de usuários cresceu e, com ele, o número de solicitações.

Para entender, o número anual de sessões em 2017 apenas no site de desktop é de 131 milhões. Em 2018, 125 milhões de 2019 são novamente 130 milhões. Adicione outros 100-200 milhões da versão móvel do site e do aplicativo móvel, e você receberá um grande número de solicitações.

Com o crescimento do projeto, o Postgres deixou de lidar com a carga, não tivemos tempo - um grande número de consultas apareceu, sob as quais não conseguimos criar um número suficiente de índices.

Percebemos que havia a necessidade de outros data warehouses que suprissem nossas necessidades e reduzissem a carga do PostgreSQL. Elasticsearch e MongoDB foram considerados como possíveis opções. Este último perdeu nos seguintes pontos:

  1. Velocidade de indexação lenta com aumento do volume de dados nos índices. No Elastic, a velocidade não depende da quantidade de dados.
  2. Nenhuma pesquisa de texto completo

Por isso, escolhemos o Elastic para nós mesmos e nos preparamos para a transição.

Mudando para Elastic


1. Iniciamos a transição com um serviço de pesquisa de ponto de venda. Nosso cliente tem um total de cerca de 70.000 pontos de venda e requer vários tipos de pesquisas no site e no aplicativo:

  • Pesquisa de texto por nome da cidade
  • Pesquisa geográfica em um determinado raio a partir de algum ponto. Por exemplo, se um usuário deseja ver quais pontos de venda estão mais próximos de sua casa.
  • Pesquise por um determinado quadrado - o usuário desenha um quadrado no mapa e é mostrado todos os pontos nesse raio.
  • Pesquise por filtros adicionais. Os pontos de venda diferem entre si no sortimento

Falando em organização, no Postgres, temos uma fonte de dados no mapa e nas notícias, e no Elastic Snapshots são feitos a partir dos dados originais. O fato é que inicialmente o Postgres não conseguiu lidar com a pesquisa por todos os critérios. Não só havia muitos índices, eles também podiam se cruzar, como o agendador do Postgres se perdeu e não entendeu qual índice usar para ele.

2. O próximo na fila foi a seção de notícias. Todos os dias, as publicações aparecem no site para que o usuário não se perca no fluxo de informações, os dados devem ser classificados antes da entrega. Para isso, é necessário pesquisar: no site, é possível pesquisar por correspondência de texto e, ao mesmo tempo, conectar filtros adicionais, pois eles também são feitos pelo Elastic.

3. Em seguida, movemos o processamento da transação. Os usuários podem comprar um produto específico no site e participar do sorteio. Após essas compras, processamos uma grande quantidade de dados, principalmente nos finais de semana e feriados. Para comparação, se em dias normais o número de compras estiver entre 1,5 e 2 milhões, então nos feriados o número pode chegar a 53 milhões.

Ao mesmo tempo, os dados precisam ser processados ​​em um tempo mínimo - os usuários não gostam de esperar por um resultado por vários dias. Você não alcança esses prazos no Postgres - geralmente obtivemos bloqueios e, enquanto processamos todas as solicitações, os usuários não podiam verificar se receberam prêmios ou não. Isso não é muito agradável para os negócios, então mudamos o processamento para o Elasticsearch.

Periodicidade


Agora as atualizações são configuradas com base em eventos, nas seguintes condições:

  1. Ponto de venda. Assim que os dados de uma fonte externa chegam, iniciamos a atualização imediatamente.
  2. Notícia. Assim que qualquer notícia é editada no site, ela é enviada automaticamente para o Elastic.

Aqui, novamente, vale mencionar as vantagens do Elastic. No Postgres, ao enviar uma solicitação, você precisa esperar até que ela processe honestamente todos os registros. Você pode enviar 10 mil registros para o Elastic e começar a trabalhar imediatamente, sem esperar até que os registros sejam distribuídos por todos os Shards. É claro que alguns Shard ou réplicas podem não ver os dados imediatamente, mas muito em breve tudo estará disponível.

Métodos de integração


Existem 2 maneiras de integrar com o Elastic:

  1. Através do cliente nativo sobre TCP. O driver nativo está morrendo gradualmente: ele não é mais suportado, possui uma sintaxe muito inconveniente. Portanto, praticamente não o usamos e tentamos abandoná-lo completamente.
  2. Por meio de uma interface HTTP na qual você pode usar as solicitações JSON e a sintaxe do Lucene. O último é um mecanismo de texto que usa o Elastic. Nesta opção, temos a capacidade de fazer um lote através de solicitações JSON por HTTP. Esta é a opção que estamos tentando usar.

Graças à interface HTTP, podemos usar bibliotecas que fornecem uma implementação assíncrona do cliente HTTP. Podemos tirar proveito do Batch e da API assíncrona, que no final oferece alto desempenho, o que ajudou muito nos dias de uma ação grande (mais sobre isso abaixo)

. Alguns números para comparar:

  • Salvando usuários que receberam prêmios no Postgres em 20 fluxos sem agrupamentos: 460.713 entradas em 42 segundos
  • Cliente elástico + reativo para 10 threads + lote para 1000 elementos: 596749 registros em 11 segundos
  • Cliente elástico + reativo para 10 threads + lote para 1000 elementos: 23801684 registros em 4 minutos

Agora, escrevemos um gerenciador de solicitações HTTP que cria JSON como Lote / não Lote e o envia por qualquer cliente HTTP, independentemente da biblioteca. Você também pode optar por enviar solicitações de forma síncrona ou assíncrona.

Em algumas integrações, ainda usamos o cliente de transporte oficial, mas isso é apenas uma questão de refatoração futura. Ao mesmo tempo, um cliente personalizado criado com base no Spring WebClient é usado para processamento.

imagem

Grande promoção


Uma vez por ano, uma grande campanha para usuários ocorre no projeto - é o mesmo Highload, pois, neste momento, trabalhamos com dezenas de milhões de usuários ao mesmo tempo.

Geralmente, os picos de carga ocorrem durante as férias, mas essa promoção é um nível completamente diferente. No ano anterior, no dia da promoção, vendemos 27.580.890 unidades de mercadorias. Os dados foram processados ​​por mais de meia hora, o que causou transtornos aos usuários. Os usuários receberam prêmios por participar, mas ficou claro que o processo precisava ser acelerado.

No início de 2019, decidimos que precisávamos do ElasticSearch. Durante um ano inteiro, organizamos o processamento dos dados recebidos no Elastic e sua saída na API do aplicativo e site móvel. Como resultado, no ano seguinte, durante a campanha, processamos 15 131 783 registros em 6 minutos.

Como temos muitas pessoas que querem comprar mercadorias e participar do sorteio de promoções, essa é uma medida temporária. Agora estamos enviando informações relevantes ao Elastic, mas no futuro planejamos transferir informações de arquivamento dos últimos meses para o Postgres, como um repositório permanente. Para não entupir o índice Elastic, que também tem suas limitações.

Conclusão / Conclusões


No momento, transferimos para a Elastic todos os serviços que desejávamos e pausamos por enquanto. Agora, estamos criando um índice no Elastic sobre o principal armazenamento persistente no Postgres, que assume a carga do usuário.

No futuro, planejamos transferir serviços se entendermos que a solicitação de dados se torna muito diversificada e é pesquisada por um número ilimitado de colunas. Isso não é mais uma tarefa para o Postgres.

Se precisamos de uma pesquisa de texto completo no funcional, ou se temos muitos critérios de pesquisa diferentes, já sabemos que isso precisa ser traduzido para o Elastic.

⌘⌘⌘


Obrigado pela leitura. Se sua empresa também usa o ElasticSearch e possui seus próprios casos de implementação, informe-nos. Será interessante saber como os outros têm :-)

All Articles