Prática de ajuste de desempenho de pesquisa de dados ES de nível de bilhão na comunidade Dewu

1. Fundo

Desde 2020, a pesquisa por resultados de anotação de conteúdo tem sido um dos principais cenários de uso de alta frequência dos negócios de back-end na comunidade. Para oferecer suporte à pesquisa de back-end complexa, armazenamos uma cópia adicional das principais informações do conteúdo da comunidade no Elasticsearch para uso como um índice secundário. Com a subdivisão, iteração e passagem do tempo no negócio de anotação, o número de documentos indexados e o RT da busca começaram a aumentar gradativamente. A seguir está o status de monitoramento atual desse índice.1.png

Este artigo apresenta como a comunidade usa IndexSorting para otimizar o desempenho de pesquisa de documentos de nível de bilhão dos 2.000 ms iniciais a 50 ms . Se você encontrar problemas e cenários semelhantes, acredito que depois de lê-lo, você poderá ganhar muito dinheiro com uma linha de código.

2. Processo de exploração

2.1 Otimização inicial

O requisito inicial é muito simples, basta buscar a exibição de paginação dinâmica mais recente . Neste momento, a implementação também é simples e rude, desde que a função seja satisfeita. A declaração de consulta é a seguinte:


{

"track_total_hits": true,

"sort": [

{

"publish_time": {

"order": "desc"

}

}

],

"size": 10

}

Como nenhuma condição de filtragem foi adicionada quando a página inicial foi carregada, tornou-se encontrar os 10 conteúdos publicados mais recentes da biblioteca de conteúdo de bilhões de níveis .

Para esta consulta, é fácil descobrir que o problema ocorre na ordenação de grandes conjuntos de resultados. Para resolver o problema, dois caminhos são naturalmente pensados:

  1. remover classificação
  2. Limitar o conjunto de resultados

Depois de pesar as demandas do usuário e os custos de desenvolvimento, decidiu-se "segurar primeiro, depois otimizar": quando o usuário abre a página inicial, a condição de filtro "publicado na última semana" é adicionada por padrão. torna-se:


{

"track_total_hits": true,

"query": {

"bool": {

"filter": [

{

"range": {

"publish_time": {

"gte": 1678550400,

"lt": 1679155200

}

}

}

]

}

},

"sort": [

{

"publish_time": {

"order": "desc"

}

}

],

"size": 10

}

Após o lançamento desta alteração, pode-se dizer que o efeito foi imediato: a velocidade de carregamento da página inicial caiu imediatamente para menos de 200ms, com um RT médio de 60ms. Essa mudança também reduziu a pressão do negócio para nós e comprou muito tempo de pesquisa para otimização subsequente.

Embora a velocidade de carregamento da página inicial de pesquisa seja obviamente mais rápida, na verdade ela não resolve o problema fundamental - a classificação de campos especificados em grandes conjuntos de resultados do ES ainda é muito lenta . Para os negócios, a experiência de algumas funções de limite na página de resultados ainda não é satisfatória, como exportação, pesquisa dinâmica completa etc. Este ponto também pode ser visto claramente no monitoramento: as consultas lentas ainda existem e são acompanhadas por um pequeno número de timeouts de interface.

2.png

Para ser honesto, nossa compreensão do ES neste período ainda é relativamente básica. Só podemos dizer que podemos usá-lo, saber sobre sharding, índice invertido e pontuação de correlação, e então acabou. Em suma, temos uma direção e começamos a alcançá-la.

2.2 Moagem fina

2.2.1 Acúmulo de conhecimento

Com os problemas que sobraram de antes, começamos a começar de novo e aprender ES do zero. Para otimizar o desempenho da pesquisa, a primeira coisa que precisamos saber é como a pesquisa funciona. Vamos pegar a pesquisa mais simples como exemplo para desmontar todo o processo de solicitação de pesquisa.

(1) Solicitação de pesquisa

{

"track_total_hits":false,

"query": {

"bool": {

"filter": [

{

"term": {

"category_id.keyword": "xxxxxxxx"

}

}

]

}

},

"size": 10

}

Consulte com precisão os documentos cujo category_id é "xxxxxxxx", busque 10 dados, nenhuma classificação ou número total é necessário

O processo geral é dividido em 3 etapas:

  1. O cliente inicia uma solicitação para Node1
  2. Como nó coordenador, o Nó1 encaminha a solicitação para cada estilhaço primário ou secundário do índice e cada estilhaço executa a consulta localmente.
  3. Cada nó retorna seus próprios dados e o nó coordenador resume e retorna ao cliente

Esse processo pode ser representado grosseiramente na figura:

3.png

Sabemos que o ES depende dos recursos fornecidos pelo Lucene. A pesquisa real ocorre no Lucene e precisamos continuar a entender o processo de pesquisa no Lucene.

(2)Luceno

Lucene contém quatro tipos básicos de dados, a saber:

  • Índice: Índice, composto por vários Documentos.
  • Documento: Composto por vários Campos, é a menor unidade de Índice e Busca.
  • Campo: Consiste em muitos termos, incluindo Nome do Campo e Valor do Campo.
  • Termo: Consiste em muitos bytes. Geralmente, cada menor unidade após a segmentação da palavra do Valor do Campo do tipo Texto é chamada de Termo.

Antes de apresentar o processo de pesquisa do índice Lucene, deixe-me falar sobre a menor unidade de armazenamento de dados que compõe o índice Lucene——Segment.

O índice Lucene é composto de muitos segmentos, e cada segmento contém o dicionário de termos do documento, a lista invertida do dicionário de termos, o armazenamento colunar DocValues ​​do documento e o índice direto. Ele pode fornecer de forma independente e direta a função de pesquisa externamente, que é quase uma versão reduzida do índice Lucene.

(3) Dicionário de termos e lista de postagem

4.png

A figura acima mostra a aparência geral do dicionário de termos e sua lista invertida. Claro, existem algumas estruturas de dados importantes aqui, como:

  • FST: índice de termos, construído na memória. Pode realizar rapidamente um único termo, intervalo de termos, prefixo de termo e consulta curinga.
  • BKD-Tree: para pesquisa rápida de tipos numéricos (incluindo pontos espaciais).
  • SkipList: a estrutura de dados da lista invertida

Há muitos detalhes aqui, e quem estiver interessado pode aprender sobre isso separadamente. Isso não afeta nosso processo geral de pesquisa, mas vou repetir aqui. Com o dicionário de termos e a lista invertida, podemos obter diretamente o conjunto de resultados que corresponda aos critérios de pesquisa, em seguida, precisamos apenas recuperar o doc inteiro do índice de encaminhamento através do docID e retorná-lo. Isso ocorre porque o disco básico do ES não será lento em teoria. Supomos que a consulta lenta ocorra na classificação. Então, o que acontece quando você adiciona uma classificação à solicitação? por exemplo:


{

"track_total_hits":false,

"query": {

"bool": {

"filter": [

{

"term": {

"category_id.keyword": "xxxxxxxx"

}

}

]

}

},

"sort": [

{

"publish_time": {

"order": "desc"

}

}

],

"size": 10

}

Os docIds obtidos por meio da lista invertida estão fora de ordem. Agora que o campo de classificação foi especificado, a maneira mais fácil e direta é extrair todos eles e classificar os 10 primeiros. Isso certamente pode alcançar o efeito, mas a eficiência é concebível. Então, como o Lucene resolve isso?

(4)DocValues

O índice invertido pode resolver o mapeamento rápido da palavra para o documento, mas precisa do mapeamento rápido do número do documento para o valor quando são necessárias operações de agregação, como classificação, ordenação e cálculo matemático dos resultados da recuperação. E o índice positivo está muito inchado, o que devo fazer?

Neste momento, vocês podem pensar diretamente em armazenamento colunar. Não há nada de errado com isso. Lucene introduziu uma estrutura de armazenamento colunar baseada em docId——DocValues

Número do documento valor da coluna mapeamento de valor de coluna
0 13/01/2023 2
1 12/01/2023 1
2 13/03/2023 3

Por exemplo, DocValues=[2023-01-13, 2023-01-12,2023-03-13] na tabela acima

Se o valor da coluna for uma string, o Lucene classificará o valor da string original de acordo com o dicionário para gerar um ID digital. Esse pré-processamento pode acelerar ainda mais a velocidade de classificação. Então temos DocValues=[2, 1, 3]

A forma de armazenamento colunar de Docvalues ​​pode acelerar nossa travessia. Neste ponto, uma solicitação de pesquisa regular para buscar os primeiros N registros pode ser considerada como a conclusão real do desmantelamento. A análise de frequência de palavras, pontuação de relevância, agregação e outras funções não são discutidas aqui, então este artigo simplifica muito todo o processo e estrutura de dados. Se você está interessado nesta parte, bem-vindo para discutir juntos.

Neste momento, o problema da classificação lenta surgiu gradualmente: embora o Docvalues ​​seja um armazenamento colunar e pré-processe valores complexos em valores simples para evitar comparações complexas durante as consultas, ele ainda não pode conter o grande conjunto de dados que precisamos classificar .

Parece que o ES está tentando o seu melhor, e parece que realmente não é bom em resolver o problema de consulta lenta em nossa cena.

No entanto, os leitores espirituais devem ter pensado que, se a lista invertida puder ser armazenada na ordem que pré-especificamos, todo o tempo de classificação poderá ser salvo .

2.2.2 Ordenação do Índice

Logo o documento oficial do ES "Como ajustar a velocidade de pesquisa" mencionou um método de otimização de pesquisa - Classificação de índice (Classificação de índice) apareceu em nosso campo de visão.

Pela descrição no documento, podemos saber que a classificação por índice melhora o desempenho da pesquisa principalmente em dois aspectos:

  1. Para consultas paralelas de várias condições (a e b e ...), a classificação de índice pode nos ajudar a armazenar documentos não qualificados juntos e ignorar um grande número de documentos não correspondentes. Mas esse truque funciona apenas para campos de baixa cardinalidade que costumam ser usados ​​para filtragem.
  2. Early break: Quando a ordem especificada pela classificação de pesquisa e a classificação de índice é a mesma, apenas os primeiros N documentos de cada segmento precisam ser comparados e os outros documentos precisam ser usados ​​apenas para o cálculo total. Por exemplo: há um carimbo de data/hora em nosso documento e geralmente precisamos pesquisar e classificar de acordo com o carimbo de data/hora. Nesse momento, se a classificação do índice especificada for consistente com a classificação da pesquisa, a eficiência da pesquisa e classificação geralmente pode ser muito melhorou.

Interrupção precoce! ! ! Era simplesmente o que faltava, então começamos a pesquisar sobre isso.

(1) Ativar classificação de índice

{

"settings": {

"index": {

"sort.field": "publish_time", // 可指定多个字段

"sort.order": "desc"

}

},

"mappings": {

"properties": {

"content_id": {

"type": "long"

},

"publish_time": {

"type": "long"

},

...

}

}

}

Como no exemplo acima, os documentos são classificados em ordem decrescente do campo publish_time quando são gravados no disco.

Nos parágrafos anteriores mencionamos repetidamente docID e índice positivo. A propósito, aqui apresentamos brevemente o relacionamento deles. Primeiro, cada documento no segmento receberá um docID. O docID começa em 0 e é atribuído sequencialmente. Quando não há IndexSorting, o docID é alocado de acordo com a ordem em que os documentos são gravados. Depois que o IndexSorting é definido, a ordem do docID é consistente com a ordem do IndexSorting.

A figura a seguir descreve a relação entre docID e índice de encaminhamento:

5.png

Então, olhe novamente para a nossa consulta original:


{

"track_total_hits":true,

"sort": [

{

"publish_time": {

"order": "desc"

}

}

],

"size": 10

}

Ao consultar no Lucene, descobriu-se que a ordem da lista invertida do conjunto de resultados é apenas na ordem decrescente de publish_time, portanto, a consulta pode retornar após os primeiros 10 dados serem consultados, o que atinge a interrupção antecipada e economiza a sobrecarga de classificação . Então, qual é o preço?

(2) Consideração

IndexSorting é diferente da classificação no momento da consulta. A essência é pré-processar os dados durante a gravação. Portanto, os campos de classificação só podem ser especificados no momento da criação e não podem ser alterados. E como os dados precisam ser classificados durante a gravação, isso também terá um certo impacto negativo no desempenho da gravação.

Mencionamos anteriormente que o próprio Lucene possui várias otimizações para classificação, portanto, se o próprio conjunto de resultados da pesquisa não tiver tantos dados, mesmo que essa função não esteja habilitada, ele ainda pode ter um bom RT.

Além disso, como o número total ainda precisa ser calculado na maioria das vezes, depois que a classificação do índice é habilitada, o processo de classificação só pode ser interrompido antecipadamente ou o número total do conjunto de resultados deve ser contado. Se você não puder verificar o número total ou obter o número total de outra maneira, poderá usar melhor esse recurso.

resumo:

  1. Para o cenário em que os N itens principais são classificados para um grande conjunto de resultados, a classificação de índice pode melhorar significativamente o desempenho da pesquisa .
  2. A classificação do índice só pode ser especificada ao criar o índice e não pode ser alterada . Se você tiver mais de um cenário de classificação de campo especificado, pode ser necessário escolher o campo de classificação com cuidado.
  3. Não obter o número total pode fazer melhor uso da classificação de índice .
  4. Habilitar a classificação de índice reduzirá o desempenho de gravação até certo ponto. Aqui está uma captura de tela dos dados do ElaticsearchBenchmarks para sua referência.

6.png

Referências: Elasticsearch Benchmarks

2.3 Efeito

Como nosso negócio está longe de atingir o gargalo de gravação do ES, há poucos cenários em que os campos de classificação são alterados com frequência. Após uma breve compensação, foi determinado que a classificação por índice era exatamente o que precisávamos, então começamos a usar dados reais online para realizar um teste de desempenho simples sobre o efeito da classificação por índice.

(1) Teste de desempenho: página inicial

7.png

(2) Teste de desempenho: outro

Aqui, depois que a classificação do índice é ativada, o teste de combinação de pesquisa de várias condições gerais e janelas de tempo é aleatório

8.png

Pode-se ver que o efeito é muito óbvio, não há pico como antes e o RT também é muito estável, então decidimos lançar oficialmente esta função.

(3) Efeito online

consulta lenta

9.png!

Comparação geral antes e depois

10.png

Basicamente, como esperávamos, o RT de pesquisa foi bastante reduzido e a consulta lenta desapareceu completamente.

2.4 Otimização posterior

No processo de exploração, na verdade, encontramos alguns outros métodos de otimização. Em vista dos custos e benefícios do desenvolvimento, alguns deles não foram totalmente aplicados ao ambiente de produção. Aqui estão alguns deles, na esperança de lhe dar alguma inspiração.

  1. Não obtenha o número total: na maioria dos cenários, não consultar o número total pode reduzir a sobrecarga e melhorar o desempenho. A interface de pesquisa após o ES 7.x não retorna o número total por padrão, o que é evidente.
  2. Regras de roteamento personalizadas: no processo de consulta acima, podemos ver que o ES pesquisará todos os fragmentos para obter os dados desejados.Se pudermos controlar a localização dos fragmentos de dados, também podemos economizar muita sobrecarga. Por exemplo: Se tivermos um grande número de cenários no futuro para verificar a dinâmica de um determinado usuário, podemos controlar o sharding por usuário, o que evita o shard polling e melhora a eficiência da pesquisa.
  3. palavra-chave: Nem todos os números devem ser armazenados como campos numéricos. Se seus valores numéricos raramente são usados ​​para consultas de intervalo, mas são frequentemente usados ​​para consultas de termos e são sensíveis à pesquisa rt. Então, a palavra-chave é o método de armazenamento mais adequado.
  4. Pré-processamento de dados: assim como o IndexSoting, se pudermos pré-processar os dados durante a gravação, também podemos economizar a sobrecarga de pesquisa. Esta combinação _ingest/pipeline pode ter efeitos inesperados.

3. Escreva no final

Acredito que todos que já viram isso podem ver que nossa otimização não envolve dificuldades técnicas muito avançadas, estamos apenas no processo de resolução de problemas e gradualmente mudamos de iniciante para iniciante. Chegar a uma vaca grande pode evitar diretamente nossos desvios desde o início, mas uma jornada de milhares de quilômetros começa com um único passo. Finalmente, aqui está um resumo de experiências e sentimentos para compartilhar com todos, esperando dar algumas referências aos iniciantes como nós.

O ES não funciona bem em cenários em que grandes conjuntos de resultados especificam a classificação de campos e devemos tentar evitar tais cenários ao usá-lo. Se isso não puder ser evitado, as configurações adequadas de IndexSorting podem melhorar muito o desempenho da classificação .

A otimização é infinita, devemos pesar custos e benefícios e concentrar recursos para resolver os problemas mais prioritários e importantes.

Texto: Kelp


Este artigo pertence ao original da tecnologia Dewu, fonte: site oficial da tecnologia Dewu

É estritamente proibido reimprimir sem a permissão da Dewu Technology, caso contrário, a responsabilidade legal será investigada de acordo com a lei! Os direitos autorais pertencem ao autor. Para reimpressão comercial, por favor, entre em contato com o autor para autorização, para reimpressão não comercial, por favor, indique a fonte.

{{o.name}}
{{m.name}}

Acho que você gosta

Origin my.oschina.net/u/5783135/blog/8821399
Recomendado
Clasificación