Estágio 8: Estrutura de serviço avançada (Capítulo 6: ElasticSearch3)

Estágio 8: Estrutura de serviço avançada (Capítulo 6: ElasticSearch3)

Capítulo Seis:ElasticSearch

Mecanismo de pesquisa distribuído 3

Insira a descrição da imagem aqui

0. Objetivos de aprendizagem

Insira a descrição da imagem aqui

1. Agregação de dados

As agregações nos permitem implementar de forma extremamente convenienteEstatísticas, análise e cálculo de dados. Por exemplo:

  • Qual marca de telefones celulares é a mais popular?
  • Qual é o preço médio, preço máximo e preço mínimo desses celulares?
  • Quais são as vendas mensais desses telefones?

sqlÉ muito mais conveniente   implementar essas funções estatísticas do que o banco de dados , e a velocidade da consulta é muito rápida e os efeitos de pesquisa em tempo real podem ser alcançados.

1.1. Tipos de agregação

Existem três tipos comuns de agregação:

  • Agregação de intervalo :Usado para agrupar documentos e contar o número de cada grupo

    • TermAggregation:de acordo comValor do campo do documentoAgrupamento, como agrupamento por valor de marca, agrupamento por país
    • Date Histogram:de acordo comescada de dataAgrupamento, como um grupo por semana ou um grupo por mês

    Os campos agregados não são segmentados

  • Agregação de métricas : usada paracalcule alguns valores, tais como: valor máximo, valor mínimo, valor médio, etc.

    • Média: valor médio
    • Máx.: Encontre o valor máximo
    • Min: Encontre o valor mínimo
    • Stats: Encontre máximo, mínimo, média, soma, etc. ao mesmo tempo
  • Agregação de pipeline :Agregar com base nos resultados de outras agregações

Perceber: Os campos participantes da agregação devem ser keyword, , 日期,数值布尔类型;
(Ou seja, os campos que participam da agregação são todos campos que não podem ser segmentados)

Insira a descrição da imagem aqui

1.2. DSL implementa agregação

  Agora queremos contar os tipos de marcas de hotéis em todos os dados, na verdade agrupamos os dados de acordo com a marca. Neste momento a agregação pode ser feita com base no nome da marca do hotel, ou seja, Bucketagregação.

1.2.1.BucketSintaxe de agregação (agregação de bucket)

A sintaxe é a seguinte:

GET /hotel/_search
{
    
    
  "size": 0,  // 设置size为0,结果中不包含文档,只包含聚合结果
  "aggs": {
    
     // 定义聚合
    "brandAgg": {
    
     //给聚合起个名字,随便起;
      "terms": {
    
     // 聚合的类型,按照字段值聚合,所以选择term,代表TermAggregation,按照文档字段值分组
        "field": "brand", // 参与聚合的字段
        "size": 20 // 希望获取的聚合结果数量
      }
    }
  }
}

acimaO que é importante é o seguinte: Nome da agregação, tipo de agregação, valor do campo ;

    "brandAgg": {
    
     //给聚合起个名字,随便起;
      "terms": {
    
     // 聚合的类型,按照字段值聚合,所以选择term
        "field": "brand", // 参与聚合的字段

O resultado é mostrado abaixo:
Insira a descrição da imagem aqui

1.2.2.Classificação de resultados de agregação

  Por padrão, Bucketa agregação conta Bucketo número de documentos dentro, indicado por _count, e segue_countClassificar em ordem decrescente

  Podemos especificar o atributo order para personalizar o método de classificação da agregação:

GET /hotel/_search
{
    
    
  "size": 0,  // 没置size为0,结果中不包含文档,只包含聚合结果
  "aggs": {
    
       // 定义聚合
    "brandAgg": {
    
       //给聚合起个名字,随便起;
      "terms": {
    
       // 聚合的类型,按照字民值聚合,所以选择term,代表TermAggregation,按照文档字段值分组
        "field": "brand",   // 参与案合的字段
        "order": {
    
    
          "_count": "asc" // 按照_count升序排列
        },
        "size": 20   // 希望获取的聚合结果数量;
      }
    }
  }
}

1.2.3.Limitar o escopo de agregação

  Por padrão, a agregação de bucket agrega todos os documentos no banco de dados de índice, mas em cenários reais, os usuários inserirão condições de pesquisa, portantoA agregação precisa ser uma agregação dos resultados da pesquisa. EntãoA agregação deve ser qualificada

pudermosLimitar o escopo dos documentos a serem agregados, basta adicionar queryuma condição:

GET /hotel/_search
{
    
    
  "query": {
    
    
    "range": {
    
    
      "price": {
    
    
        "lte": 200 // 只对200元以下的文档聚合
      }
    }
  }, 
  "size": 0, 
  "aggs": {
    
    
    "brandAgg": {
    
    
      "terms": {
    
    
        "field": "brand",
        "size": 20
      }
    }
  }
}

Desta vez, o número de marcas agregadas foi significativamente menor:
Insira a descrição da imagem aqui

1.2.4.MetricSintaxe de agregação (agregação de métrica)

  Acima agrupamos os hotéis de acordo com as marcas, formando baldes. Agora precisamos fazer cálculos sobre os hotéis do intervalo,Obtenha a avaliação do usuário de cada marca , valores equivalentesminmaxavg

  Isso requer o uso de Metricagregação, como agregação de estatísticas: você pode obter resultados como min, maxe assim por diante.avg

A sintaxe é a seguinte:

GET /hotel/_search
{
    
    
  "size": 0, 
  "aggs": {
    
     //定义聚合
    "brandAgg": {
    
       //给聚合起个名字,随便起
      "terms": {
    
       //terms聚合
        "field": "brand", 
        "size": 20  //希望获取的聚合结果数量
      },
      "aggs": {
    
     // 是brands聚合的子聚合,也就是分组后对每组分别计算
        "score_stats": {
    
     // 聚合名称
          "stats": {
    
     // 聚合类型,这里stats可以计算min、max、avg等
            "field": "score" // 聚合字段,这里是score
          }
        }
      }
    }
  }
}

  Esta score_statsagregação é uma subagregação aninhadabrandAgg na agregação . Porque precisamos calculá-lo separadamente em cada intervalo.
Insira a descrição da imagem aqui

  Além disso, também podemos classificar os resultados da agregação, por exemplo, pela pontuação média do hotel em cada intervalo:
Insira a descrição da imagem aqui

1.2.5.resumo

aggsRepresenta agregação, e query同级qual queryé o seu papel neste momento?

  • Limitar o escopo dos documentos a serem agregados

Três elementos são necessários para agregação:

  • Nome da agregação
  • Tipo de agregação
  • Campos agregados

As propriedades configuráveis ​​agregadas são:

  • size:Especifique o número de resultados de agregação
  • order: Especifique o método de classificação dos resultados de agregação
  • field:Especifique o campo de agregação

Insira a descrição da imagem aqui

1.3. RestAPI implementa agregação

1.3.1.Sintaxe da API

Condições de agregaçãoequerydoençaNo mesmo nível, você precisa usá-lo request.source()para especificar condições de agregação.
Sintaxe das condições de agregação:
Insira a descrição da imagem aqui
os resultados da agregação também são diferentes dos resultados da consulta e a API também é especial. Mas o mesmo JSON é analisado camada por camada:
Insira a descrição da imagem aqui

1.3.2.Necessidades empresariais

Caso: Definir métodos em IUserService para obter agregação de marcas, cidades e classificações por estrelas.
Requisitos: A marca, cidade e outras informações na página de pesquisa não devem ser codificadas na página, mas obtidas pela agregação dos dados do hotel em a biblioteca de índice:
Insira a descrição da imagem aqui

Análise:
  atualmente, a lista de cidades, a lista de estrelas e a lista de marcas na página são codificadas e não serão alteradas com os resultados da pesquisa. Mas quando as condições de pesquisa do usuário mudam, os resultados da pesquisa mudam de acordo.

  Por exemplo, se um usuário pesquisar "Oriental Pearl Tower", o hotel pesquisado deverá estar próximo à Oriental Pearl Tower em Xangai. Portanto, a cidade só pode ser Xangai. No momento, a lista de cidades não deve exibir informações como Pequim , Shenzhen e Hangzhou.

  Quer dizer,Quais cidades estão incluídas nos resultados da pesquisa, quais cidades devem ser listadas na página; quais marcas estão incluídas nos resultados da pesquisa, quais marcas devem ser listadas na página

  Como posso saber quais marcas estão incluídas nos meus resultados de pesquisa? Como posso saber quais cidades estão incluídas nos meus resultados de pesquisa?

  Use a função de agregação e a agregação de bucket para agrupar os documentos nos resultados da pesquisa com base em marcas e cidades, para que você possa saber quais marcas e quais cidades estão incluídas.

  Como os resultados da pesquisa são agregados, a agregação éagregação com escopo,Por outras palavras, as condições de agregação são consistentes com as condições de pesquisa de documentos.

  Olhando para o navegador, você pode descobrir que o front-end realmente emitiu tal solicitação:
Insira a descrição da imagem aqui
os parâmetros da solicitação são exatamente iguais aos parâmetros do documento de pesquisa .

O tipo de valor de retorno é o resultado final a ser exibido na página:
Insira a descrição da imagem aqui
o resultado é uma Mapestrutura:

  • keyÉ uma string, cidade, classificação por estrelas, marca, preço
  • valueé uma coleção, como os nomes de várias cidades

1.3.3.Realização de negócios

Insira a descrição da imagem aqui

Adicione um método ao cn.itcast.hotel.webpacote HotelControllercom os seguintes requisitos:

  • Método de solicitação:POST
  • Caminho da solicitação:/hotel/filters
  • Parâmetros de solicitação: RequestParams, consistentes com os parâmetros de busca de documentos
  • Tipo de valor de retorno:Map<String, List<String>>

Código:

    @PostMapping("filters")
    public Map<String, List<String>> getFilters(@RequestBody RequestParams params){
    
    
        return hotelService.getFilters(params);
    }

O método chamado aqui ainda não IHotelService中foi getFiltersimplementado.

Defina o novo método em cn.itcast.hotel.service.IHotelService:

Map<String, List<String>> filters(RequestParams params);

Implemente este método em cn.itcast.hotel.service.impl.HotelService:

@Override
public Map<String, List<String>> filters(RequestParams params) {
    
    
    try {
    
    
        // 1.准备Request
        SearchRequest request = new SearchRequest("hotel");
        // 2.准备DSL
        // 2.1.query只限定范围
        buildBasicQuery(params, request);
        // 2.2.设置size
        request.source().size(0);
        // 2.3.聚合
        buildAggregation(request);
        // 3.发出请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 4.解析结果
        Map<String, List<String>> result = new HashMap<>();
        Aggregations aggregations = response.getAggregations();
        // 4.1.根据品牌名称,获取品牌结果
        List<String> brandList = getAggByName(aggregations, "brandAgg");
        result.put("品牌", brandList);
        // 4.2.根据品牌名称,获取品牌结果
        List<String> cityList = getAggByName(aggregations, "cityAgg");
        result.put("城市", cityList);
        // 4.3.根据品牌名称,获取品牌结果
        List<String> starList = getAggByName(aggregations, "starAgg");
        result.put("星级", starList);

        return result;
    } catch (IOException e) {
    
    
        throw new RuntimeException(e);
    }
}

private void buildAggregation(SearchRequest request) {
    
    
    request.source().aggregation(AggregationBuilders
                                 .terms("brandAgg")
                                 .field("brand")
                                 .size(100)
                                );
    request.source().aggregation(AggregationBuilders
                                 .terms("cityAgg")
                                 .field("city")
                                 .size(100)
                                );
    request.source().aggregation(AggregationBuilders
                                 .terms("starAgg")
                                 .field("starName")
                                 .size(100)
                                );
}

private List<String> getAggByName(Aggregations aggregations, String aggName) {
    
    
    // 4.1.根据聚合名称获取聚合结果
    Terms brandTerms = aggregations.get(aggName);
    // 4.2.获取buckets
    List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
    // 4.3.遍历
    List<String> brandList = new ArrayList<>();
    for (Terms.Bucket bucket : buckets) {
    
    
        // 4.4.获取key
        String key = bucket.getKeyAsString();
        brandList.add(key);
    }
    return brandList;
}

Insira a descrição da imagem aqui

2. Preenchimento automático _

Insira a descrição da imagem aqui

Quando o usuário insere um caractere na caixa de pesquisa, devemos solicitar termos de pesquisa relacionados ao caractere, conforme mostrado na figura:
Insira a descrição da imagem aqui
Esta função que solicita entradas completas com base nas letras inseridas pelo usuário é de preenchimento automático.

Como precisa ser inferido com base nas letras Pinyin, a função de segmentação de palavras Pinyin é usada.

2.1. Segmentador de palavras Pinyin

  Para conseguir o preenchimento baseado em cartas, o documento deve ser segmentado de acordo com o Pinyin. Acontece que elasticsearchexiste . Endereço: https://github.com/medcl/elasticsearch-análise-pinyin
Insira a descrição da imagem aqui

Os materiais pré-curso também fornecem um pacote de instalação para o segmentador de palavras Pinyin:
Insira a descrição da imagem aqui
o método de instalação é o mesmo do segmentador de palavras IK, que é dividido em três etapas:

​ ①Descompacte
​ ②Fazer upload para o diretório elasticsearchna máquina virtual​ ③Reiniciar​ ④Testeplugin
elasticsearch

Para etapas de instalação detalhadas, consulte: Processo de instalação do segmentador de palavras IK .

O uso do teste é o seguinte:

POST /_analyze
{
    
    
  "text": "如家酒店还不错",  #要分词的内容;
  "analyzer": "pinyin"   #分词器
}

resultado:
Insira a descrição da imagem aqui

2.2. Segmentador de palavras personalizado

  O tokenizer Pinyin padrão separará cada caractere chinês em Pinyin, e o que queremos é que cada entrada forme um grupo de Pinyin, então precisamos fazer algumas modificações no tokenizer Pinyin.Personalização personalizada,formaTokenizador personalizado

elasticsearchO segmentador de palavras do meio ( analyzer) consiste em três partes:

  • character filters: tokenizerProcesse o texto antes. Por exemplo, exclua caracteres, substitua caracteres
  • tokenizer: Recorte o texto em termos de acordo com certas regras ( term). Por exemplo keyword, não há segmentação de palavras; eik_smart
  • tokenizer filter: tokenizerprocesse ainda mais as entradas de saída. Por exemplo, conversão de maiúsculas e minúsculas, processamento de sinônimos, processamento pinyin, etc.

Quando o documento é segmentado, o documento será processado por estas três partes em sequência:
Insira a descrição da imagem aqui

Podemos configurar um analisador personalizado (segmentador de palavras) por meio de configurações ao criar uma biblioteca de índices:
a sintaxe para declarar um tokenizer customizado é a seguinte:

PUT /test   //创建名为test的索引库
{
    
    

  "settings": {
    
       //定义索引库的分词器的
    "analysis": {
    
    
    
      "analyzer": {
    
     // 自定义分词器
        "my_analyzer": {
    
      // 分词器名称
          "tokenizer": "ik_max_word",
          "filter": "py"
        }
      },
      
      "filter": {
    
     // 自定义tokenizer filter
        "py": {
    
     // 过滤器名称
          "type": "pinyin", // 过滤器类型,这里是pinyin
		  "keep_full_pinyin": false,      //解决单个字拼的问题;
          "keep_joined_full_pinyin": true,      //全拼
          "keep_original": true,      //保留中文
          "limit_first_letter_length": 16,
          "remove_duplicated_term": true,
          "none_chinese_pinyin_tokenize": false
        }
      }
      
    }
  },



  "mappings": {
    
          //mappings映射时
    "properties": {
    
    
      "name": {
    
    
        "type": "text",
        "analyzer": "my_analyzer",      //name使用自定义的my_analyzer分词器;analyzer在创建索引时使用
        "search_analyzer": "ik_smart"      //search_analyzer在搜索索引时使用;
      }
    }
  }
}

Teste: (Os resultados incluem pinyin e caracteres chineses)
Insira a descrição da imagem aqui

Resumir:

Como usar o tokenizador Pinyin?

  • ① Baixe pinyino segmentador de palavras
  • ②Descompacte e coloque-o elasticsearchno plugindiretório
  • ③Reiniciar

Como personalizar o tokenizador?

  • ① Ao criar uma biblioteca de índice, settingsconfigure-a e pode incluir três partes
  • character filter
  • tokenizer
  • filter

O que devo prestar atenção ao usar o segmentador de palavras Pinyin?

  • Para evitar a busca por homófonos, use o tokenizer Pinyin ao criar o índice; não use o tokenizer Pinyin ao pesquisar.

2.3.Consulta de preenchimento automático

elasticsearchA consulta do Sugestor de conclusão é fornecida para implementar a função de conclusão automática. Esta consulta corresponderá e retornará termos que começam com o que o usuário inseriu. Para melhorar a eficiência do preenchimento das consultas, existem algumas restrições quanto aos tipos de campos do documento:

  • Os campos participantes da consulta de preenchimento devem ser completiondo tipo
  • O conteúdo do campo geralmente é uma matriz formada por múltiplas entradas usadas para preenchimento.

Por exemplo, uma biblioteca de índice como esta:

// 创建索引库
PUT test
{
    
    
  "mappings": {
    
    
    "properties": {
    
    
      "title":{
    
    
        "type": "completion"
      }
    }
  }
}

Em seguida insira os seguintes dados:

// 示例数据
POST test/_doc
{
    
    
  "title": ["Sony", "WH-1000XM3"]
}
POST test/_doc
{
    
    
  "title": ["SK-II", "PITERA"]
}
POST test/_doc
{
    
    
  "title": ["Nintendo", "switch"]
}

A instrução DSL de consulta é a seguinte:

// 自动补全查询
GET /test/_search
{
    
    
  "suggest": {
    
    
    "title_suggest": {
    
       //随意起的名称;
      "text": "s", // 关键字
      "completion": {
    
          //自动补全的类型
        "field": "title", // 补全查询的字段
        "skip_duplicates": true, // 跳过重复的
        "size": 10 // 获取前10条结果
      }
    }
  }
}

resumo:
Insira a descrição da imagem aqui

2.4. Implementar o preenchimento automático da caixa de pesquisa de hotéis

  Agora, nossa hotelbiblioteca de índice não configurou um segmentador de palavras Pinyin e precisamos modificar a configuração na biblioteca de índice. mas nós sabemosA biblioteca de índices não pode ser modificada, só pode ser excluída e recriada.

  Além disso, precisamos adicionar um campo para preenchimento automático, colocar marca, sugestão, cidade, etc. como um prompt de preenchimento automático.

Então, para resumir, nósAs coisas que precisam ser feitas incluem

  1. Modifique a estrutura do banco de dados de índice de hotéis e configure um segmentador de palavras Pinyin personalizado

  2. Modifique o nome e todos os campos da biblioteca de índice e use um segmentador de palavras personalizado

  3. A biblioteca de índice adiciona uma nova sugestão de campo, cujo tipo é de conclusão e usa um segmentador de palavras personalizado.

  4. Adicione um campo de sugestão à classe HotelDoc, contendo marca e negócio

  5. Reimportar dados para o banco de dados do hotel

2.4.1.Modificar estrutura de mapeamento de hotel

código mostrado abaixo:

// 酒店数据索引库
PUT /hotel
{
    
    



  "settings": {
    
          //定义分词器
    "analysis": {
    
    
    
      "analyzer": {
    
    
      
        "text_anlyzer": {
    
        //全文检索使用的分词器
          "tokenizer": "ik_max_word",
          "filter": "py"
        },
        "completion_analyzer": {
    
          //自动补全使用的分词器
          "tokenizer": "keyword",
          "filter": "py"
        }
        
      },
      
      "filter": {
    
    
        "py": {
    
    
          "type": "pinyin",
          "keep_full_pinyin": false,
          "keep_joined_full_pinyin": true,
          "keep_original": true,
          "limit_first_letter_length": 16,
          "remove_duplicated_term": true,
          "none_chinese_pinyin_tokenize": false
        }
      }
      
    }
  },



  "mappings": {
    
    
    "properties": {
    
    
      "id":{
    
    
        "type": "keyword"
      },
      "name":{
    
    
        "type": "text",
        "analyzer": "text_anlyzer",      //创建索引时使用的分词器
        "search_analyzer": "ik_smart",      //搜索时的分词器
        "copy_to": "all"
      },
      "address":{
    
    
        "type": "keyword",
        "index": false
      },
      "price":{
    
    
        "type": "integer"
      },
      "score":{
    
    
        "type": "integer"
      },
      "brand":{
    
    
        "type": "keyword",
        "copy_to": "all"
      },
      "city":{
    
    
        "type": "keyword"
      },
      "starName":{
    
    
        "type": "keyword"
      },
      "business":{
    
    
        "type": "keyword",
        "copy_to": "all"
      },
      "location":{
    
    
        "type": "geo_point"
      },
      "pic":{
    
    
        "type": "keyword",
        "index": false
      },
      "all":{
    
    
        "type": "text",
        "analyzer": "text_anlyzer",      //创建索引时使用的分词器"text_anlyzer"
        "search_analyzer": "ik_smart"      //搜索时使用的分词器"ik_smart"
      },
      "suggestion":{
    
          //自动补全的字段
          "type": "completion",
          "analyzer": "completion_analyzer"   //分词器;
      }
    }
  }
}

2.4.2.Modificar entidade HotelDoc

  HotelDocÉ necessário adicionar um campo para preenchimento automático, o conteúdo pode ser marca do hotel, cidade, bairro comercial e outras informações. De acordo com os requisitos dos campos de preenchimento automático, é melhor ser uma matriz desses campos.

  Portanto, HotelDocadicionamos um suggestioncampo do tipo List<String>e, em seguida, colocamos informações como brand, e assim por diante.citybusiness

código mostrado abaixo:

package cn.itcast.hotel.pojo;

@Data
@NoArgsConstructor
public class HotelDoc {
    
    
    private Long id;
    private String name;
    private String address;
    private Integer price;
    private Integer score;
    private String brand;
    private String city;
    private String starName;
    private String business;
    private String location;
    private String pic;
    private Object distance;
    private Boolean isAD;
    
    private List<String> suggestion;

    public HotelDoc(Hotel hotel) {
    
    
        this.id = hotel.getId();
        this.name = hotel.getName();
        this.address = hotel.getAddress();
        this.price = hotel.getPrice();
        this.score = hotel.getScore();
        this.brand = hotel.getBrand();
        this.city = hotel.getCity();
        this.starName = hotel.getStarName();
        this.business = hotel.getBusiness();
        this.location = hotel.getLatitude() + ", " + hotel.getLongitude();
        this.pic = hotel.getPic();
        // 组装suggestion
        if(this.business.contains("/")){
    
    
            // business有多个值,需要切割
            String[] arr = this.business.split("/"); //数组;
            // 添加元素
            this.suggestion = new ArrayList<>();
            this.suggestion.add(this.brand);
            Collections.addAll(this.suggestion, arr); //批量添加,将数组中的元素一个一个的添加进集合;
        }else {
    
    
            this.suggestion = Arrays.asList(this.brand, this.business); //集合
        }
    }
}

Insira a descrição da imagem aqui

2.4.3. Reimportação

Execute novamente a função de importação de dados escrita anteriormente e você verá que os novos dados do hotel contêm sugestões:
Insira a descrição da imagem aqui

2.4.4.JavaAPI para consultas de preenchimento automático

Anteriormente, aprendemos o DSL para conclusão automática de consultas, mas não aprendemos a API Java correspondente. Aqui está um exemplo:
Insira a descrição da imagem aqui

Os campos da consulta de conclusão acima devem ser escritos como seus
Insira a descrição da imagem aqui

Os resultados do preenchimento automático também são bastante especiais. O código analisado é o seguinte:
Insira a descrição da imagem aqui

2.4.5.Implementar o preenchimento automático da caixa de pesquisa

Olhando para a página front-end, podemos descobrir que quando digitamos na caixa de entrada, o front-end iniciará ajaxuma solicitação:
Insira a descrição da imagem aqui
o valor de retorno é uma coleção de termos preenchidos, do tipoList<String>

1) Adicione uma nova interface cn.itcast.hotel.webao pacote HotelControllerpara receber novas solicitações:

@GetMapping("suggestion")
public List<String> getSuggestions(@RequestParam("key") String prefix) {
    
    
    return hotelService.getSuggestions(prefix);
}

2) Adicione métodos ao cn.itcast.hotel.servicepacote :IhotelService

List<String> getSuggestions(String prefix);

3) cn.itcast.hotel.service.impl.HotelServiceImplemente o método em:

@Override
public List<String> getSuggestions(String prefix) {
    
          //prefix是前端传过来的参数,是关键字
    try {
    
    
        // 1.准备Request
        SearchRequest request = new SearchRequest("hotel");
        // 2.准备DSL
        request.source().suggest(new SuggestBuilder().addSuggestion(
            "suggestions",
            SuggestBuilders.completionSuggestion("suggestion")   //补全字段
            .prefix(prefix)  //前段传过来的参数,是关键字
            .skipDuplicates(true)
            .size(10)
        ));
        // 3.发起请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 4.解析结果
        Suggest suggest = response.getSuggest();
        // 4.1.根据补全查询名称,获取补全结果
        CompletionSuggestion suggestions = suggest.getSuggestion("suggestions");
        // 4.2.获取options
        List<CompletionSuggestion.Entry.Option> options = suggestions.getOptions();
        // 4.3.遍历
        List<String> list = new ArrayList<>(options.size());
        for (CompletionSuggestion.Entry.Option option : options) {
    
    
            String text = option.getText().toString();
            list.add(text);
        }
        return list;
    } catch (IOException e) {
    
    
        throw new RuntimeException(e);
    }
}

3. Sincronização de dados

  elasticsearchOs dados do hotel vêm do mysqlbanco de dados, portanto mysql, quando os dados mudam, elasticsearcheles também devem mudar. Esta é a sincronização de dadoselasticsearch entremysql
Insira a descrição da imagem aqui

3.1. Análise de ideias

Existem três soluções comuns de sincronização de dados:

  • Chamada síncrona
  • Notificação assíncrona
  • monitorbinlog

3.1.1.Chamada síncrona

Opção 1: chamada síncrona
Insira a descrição da imagem aqui

As etapas básicas são as seguintes:

  • hotel-demoFornece uma interface externa para modificar elasticsearchos dados em
  • Serviços de gestão hoteleira estão sendo concluídosOperações de banco de dadosDepois disso, chame diretamente hotel-demoa interface fornecida,

3.1.2. Notificação assíncrona

Opção 2: notificação assíncrona
Insira a descrição da imagem aqui

O processo é como se segue:

  • hotel-adminApós mysqladicionar, excluir e modificar dados do banco de dados, envie MQuma mensagem
  • hotel-demoMonitore e conclua a modificação dos dados MQapós receber a mensagemelasticsearch

3.1.3.monitorbinlog

Opção 3: binlog
Insira a descrição da imagem aqui
O processo de monitoramento é o seguinte:

  • Ative mysqla binlogfunção
  • mysqlAs operações concluídas de adição, exclusão e modificação serão registradas binlogem
  • hotel-demoCom base no canalmonitoramento binlogdas alterações, elasticsearcho conteúdo é atualizado em tempo real

3.1.4.Selecionar

Método 1: chamada síncrona

  • Vantagens: simples de implementar, bruto
  • Desvantagens: alto grau de acoplamento de negócios

Método 2: notificação assíncrona

  • Vantagens: baixo acoplamento, dificuldade média de implementação
  • Desvantagens: Confie na confiabilidade do mq

Método 3: Monitoramentobinlog

  • Vantagens: Serviços de dissociação completa
  • Desvantagens: A abertura binlogaumenta a carga do banco de dados e alta complexidade de implementação

3.2. Implementar sincronização de dados

Utilize MQimplementação mysqle elasticsearchsincronização de dados

3.2.1. Ideias

  Utilize o projeto disponibilizado no material pré-curso hotel-admincomo um microsserviço para gestão hoteleira. Quando os dados do hotel são adicionados, excluídos ou modificados, as elasticsearchmesmas operações devem ser realizadas para os dados do centro.

etapa:

  • Importe o projeto fornecido pelos materiais pré-curso hotel-admin, inicie e teste os dados do hotelCRUD
  • declarar exchange(switch), queue(fila),RoutingKey
  • Conclua o envio da mensagem no hotel-admincampo adicionar, excluir e modificar negócios em
  • hotel-demoConclua o monitoramento de mensagens e atualize elasticsearchos dados em
  • Iniciar e testar a funcionalidade de sincronização de dados

3.2.2.Demonstração de importação

Importe os projetos fornecidos pelos materiais pré-aula hotel-admin:
Insira a descrição da imagem aqui
após a execução, visitehttp://localhost:8099
Insira a descrição da imagem aqui

Inclui CRUDfunções de hotel:
Insira a descrição da imagem aqui

3.2.3. Declarar switches e filas

MQA estrutura é mostrada na figura:
Insira a descrição da imagem aqui
No tutorial, o switch e a fila são declarados hotel-demono consumidor;

1)Introduzir dependências

hotel-adminDependências hotel-demointroduzidas em rabbitmq:

<!--amqp-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

1)amqpendereço configurado

Endereço configurado no .ymlarquivo amqp:
Insira a descrição da imagem aqui

2)Declarar o nome do switch de fila

Crie uma nova classe no pacote em hotel-admine :hotel-democn.itcast.hotel.constatntsMqConstants

package cn.itcast.hotel.constatnts;

    public class MqConstants {
    
    
    /**
     * 交换机
     */
    public final static String HOTEL_EXCHANGE = "hotel.topic";
    /**
     * 监听新增和修改的队列
     */
    public final static String HOTEL_INSERT_QUEUE = "hotel.insert.queue";
    /**
     * 监听删除的队列
     */
    public final static String HOTEL_DELETE_QUEUE = "hotel.delete.queue";
    /**
     * 新增或修改的RoutingKey
     */
    public final static String HOTEL_INSERT_KEY = "hotel.insert";
    /**
     * 删除的RoutingKey
     */
    public final static String HOTEL_DELETE_KEY = "hotel.delete";
}

3)Declarar o switch de fila (definir a fila do switch, relacionamento de ligação routingKey)

Em hotel-demo, cn.itcast.hotel.configdefina a classe de configuração no pacote MqConfige declare a fila e o switch:

package cn.itcast.hotel.config;

@Configuration
public class MqConfig {
    
    
    @Bean
    public TopicExchange topicExchange(){
    
          //交换机
        return new TopicExchange(MqConstants.HOTEL_EXCHANGE, true, false); //true代表持久化
    }

    @Bean
    public Queue insertQueue(){
    
          //增加和修改的队列
        return new Queue(MqConstants.HOTEL_INSERT_QUEUE, true);
    }

    @Bean
    public Queue deleteQueue(){
    
          //删除的队列;
        return new Queue(MqConstants.HOTEL_DELETE_QUEUE, true);
    }

    @Bean
    public Binding insertQueueBinding(){
    
       //绑定关系;
    //insertQueue()队列绑定到topicExchange()交换机,使用MqConstants.HOTEL_INSERT_KEY这个RoutingKey
        return BindingBuilder.bind(insertQueue()).to(topicExchange()).with(MqConstants.HOTEL_INSERT_KEY); 
    }

    @Bean
    public Binding deleteQueueBinding(){
    
       //绑定关系;
       //deleteQueue()队列绑定到topicExchange()交换机,使用MqConstants.HOTEL_DELETE_KEY这个RoutingKey
        return BindingBuilder.bind(deleteQueue()).to(topicExchange()).with(MqConstants.HOTEL_DELETE_KEY);
    }
}

3.2.4.Enviar mensagens MQ

  Sob o pacote hotel-demodo generalcn.itcast.hotel.constatntsDeclarar o nome do switch de filaMqConstantsCopie a classe hotel-adminpara cn.itcast.hotel.constatntso pacote para que não haja erros ao usar vários nomes; adicione
  também as mesmas dependências e configure o mesmo endereço ;3.2.3amqp

Envie mensagens respectivamente no hotel-adminnegócio de adicionar, excluir e modificar MQ: ( na classe do pacote hotel-admindo projeto ):cn.itcast.hotel.webHotelController

Injete o que é necessário para enviar a mensagem api:
Insira a descrição da imagem aqui

Insira a descrição da imagem aqui

3.2.5.Receber mensagens MQ

hotel-demoMQO que fazer ao receber uma mensagem inclui:

  • 新增hotelMensagem: de acordo com as informações idde consulta passadas hotel, adicione um dado ao banco de dados de índice
  • 删除Mensagem: Exclua um dado no banco de dados de índice com base no hotelpassadoid

1) Primeiro adicione e exclua serviços hotel-demodo cn.itcast.hotel.servicepacoteIHotelService

void deleteById(Long id);

void insertById(Long id);

2) Implementar o negócio do hotel-demopacote :cn.itcast.hotel.service.implHotelService

@Override
public void deleteById(Long id) {
    
    
    try {
    
    
        // 1.准备Request
        DeleteRequest request = new DeleteRequest("hotel", id.toString());
        // 2.发送请求
        client.delete(request, RequestOptions.DEFAULT);
    } catch (IOException e) {
    
    
        throw new RuntimeException(e);
    }
}

@Override
public void insertById(Long id) {
    
    
    try {
    
    
        // 0.根据id查询酒店数据
        Hotel hotel = getById(id);
        // 转换为文档类型
        HotelDoc hotelDoc = new HotelDoc(hotel);

        // 1.准备Request对象
        IndexRequest request = new IndexRequest("hotel").id(hotel.getId().toString());
        // 2.准备Json文档
        request.source(JSON.toJSONString(hotelDoc), XContentType.JSON);
        // 3.发送请求
        client.index(request, RequestOptions.DEFAULT);
    } catch (IOException e) {
    
    
        throw new RuntimeException(e);
    }
}

3) Escreva um ouvinte e adicione uma nova classe
no hotel-demopacote cn.itcast.hotel.mq:

package cn.itcast.hotel.mq;

@Component
public class HotelListener {
    
    
    @Autowired
    private IHotelService hotelService;

    /**
     * 监听酒店新增或修改的业务
     * @param id 酒店id
     */
    @RabbitListener(queues = MqConstants.HOTEL_INSERT_QUEUE)
    public void listenHotelInsertOrUpdate(Long id){
    
    
        hotelService.insertById(id);
    }

    /**
     * 监听酒店删除的业务
     * @param id 酒店id
     */
    @RabbitListener(queues = MqConstants.HOTEL_DELETE_QUEUE)
    public void listenHotelDelete(Long id){
    
    
        hotelService.deleteById(id);
    }
}

4. Aglomerado

Insira a descrição da imagem aqui

Ao fazer o armazenamento de dados em uma única máquina elasticsearch, você inevitavelmente enfrentará dois problemas:Problema de armazenamento em massa de dadosProblema de ponto único de falha

  • Problema de armazenamento em massa de dados: Divida logicamente a biblioteca de índice em N fragmentos ( shard) e armazene-os em vários nós
    Insira a descrição da imagem aqui

  • Problema de ponto único de falha: Faça backup de dados fragmentados em nós diferentes ( replica )

Conceitos relacionados ao cluster ES :

  • Cluster: um grupo de nós com um nome de cluster comum.

  • : uma instância do Elasticsearch no cluster

  • Fragmentação : O índice pode ser dividido em diferentes partes para armazenamento, chamado fragmentação. Em um ambiente de cluster, diferentes fragmentos de um índice podem ser divididos em nós diferentes

    Resolva o problema: A quantidade de dados é muito grande e a capacidade de armazenamento em um único ponto é limitada.
    Insira a descrição da imagem aqui

    Aqui, dividimos os dados em 3 fatias: shard0, shard1, shard2

  • Fragmento primário ( Primary shard): relativo à definição de fragmentos de réplica.

  • Fragmentos de réplica ( Replica shard) Cada fragmento primário pode ter uma ou mais réplicas e os dados são iguais aos do fragmento primário.

  O backup de dados pode garantir alta disponibilidade, mas se você fizer backup de cada fragmento, o número de nós necessários dobrará e o custo será muito alto!
Para encontrar um equilíbrio entre alta disponibilidade e custo, podemos fazer o seguinte:

  • Primeiro, os dados são fragmentados e armazenados em nós diferentes.
  • Em seguida, faça backup de cada fragmento e coloque-o no outro nó para concluir o backup mútuo.

Isso pode reduzir bastante o número de nós de serviço necessários. Conforme mostrado na figura, tomamos 3 fragmentos e um backup para cada fragmento como exemplo:
Insira a descrição da imagem aqui
Agora, cada fragmento tem 1 backup, armazenado em 3 nós:

  • node0: salva os fragmentos 0 e 1
  • node1: salva os fragmentos 0 e 2
  • node2: fragmentos salvos 1 e 2

4.1. Construir cluster ES

Documentos de referência para materiais pré-curso :
Insira a descrição da imagem aqui

Capítulo 4:
Insira a descrição da imagem aqui

4.2. Problema de cluster com cérebro dividido

4.2.1. Divisão de responsabilidades do cluster

elasticsearchOs nós de cluster médios têm responsabilidades diferentes:
Insira a descrição da imagem aqui

Por padrão, qualquer nó no cluster possui as quatro funções acima.
Mas um cluster real deve separar as responsabilidades do cluster

  • master节点: Altos requisitos de CPU, mas poucos requisitos de memória
  • data节点: Altos requisitos para CPU e memória
  • coordinating节点: Altos requisitos de largura de banda de rede e CPU

A separação de tarefas nos permite alocar diferentes hardwares para implantação de acordo com as necessidades dos diferentes nós. E evite interferência mútua entre empresas.

Uma divisão típica esdas responsabilidades do cluster é a seguinte:
Insira a descrição da imagem aqui

4.2.2.problema de cérebro dividido

O cérebro dividido é causado pela desconexão dos nós do cluster.
Por exemplo, em um cluster, o nó mestre perde contato com outros nós:
Insira a descrição da imagem aqui
neste momento, node2se node3for considerado node1inativo, ele reelegerá o mestre:
Insira a descrição da imagem aqui
quando o nó3 for eleito, o cluster continuará prestando serviços ao mundo exterior, node2 e node3 formam seu próprio cluster e node1 forma seu próprio cluster.Os dados dos dois clusters estão fora de sincronia e ocorrem diferenças de dados.

Quando a rede é restaurada, porque há dois nós mestres no cluster, o status do cluster é inconsistente e ocorre uma situação de divisão cerebral:
Insira a descrição da imagem aqui
  Solução para dividir o cérebroSim, obrigatórioO número de votos deve exceder (número de nós elegíveis + 1)/2 para ser eleito líder., portanto, o número de nós elegíveis é preferencialmente um número ímpar. O item de configuração correspondente é Discovery.zen.minimum_master_nodes, que se tornou a configuração padrão após es7.0, portanto, geralmente não ocorrem problemas de cérebro dividido.

  Por exemplo: para um cluster formado por 3 nós, os votos devem exceder (3+1)/2, que é 2 votos. node3 recebeu votos de node2 e node3 e foi eleito líder. Node1 tem apenas 1 voto próprio e não foi eleito. Ainda há apenas um nó mestre no cluster e não há cérebro dividido.

4.2.3. Resumo

master eligibleQual é a função dos nós?

  • Participe da eleição do líder do cluster
  • O nó mestre pode gerenciar o status do cluster, gerenciar informações de fragmentos e lidar com solicitações para criar e excluir bibliotecas de índice.

dataQual é a função dos nós?

  • CRUD de dados

coordinatorQual é a função dos nós?

  • Rotear solicitações para outros nós
  • Combine os resultados da consulta e devolva-os ao usuário

4.3. Armazenamento distribuído em cluster

Quando um novo documento é adicionado, ele deve ser salvo em fragmentos diferentes para garantir o equilíbrio dos dados. Então, coordinating nodecomo determinar em qual fragmento os dados devem ser armazenados?

4.3.1. Teste de armazenamento fragmentado

Insira três dados:
Insira a descrição da imagem aqui

Insira a descrição da imagem aqui

Insira a descrição da imagem aqui
No teste, você pode ver que os três dados estão em fragmentos diferentes:
Insira a descrição da imagem aqui

resultado:
Insira a descrição da imagem aqui

4.3.2. Princípio do armazenamento fragmentado

elasticsearchUm algoritmo é usado hashpara calcular em qual fragmento o documento deve ser armazenado:
Insira a descrição da imagem aqui

ilustrar:

  • _routingO padrão é documentoid
  • O algoritmo está relacionado ao número de fragmentos, portanto, uma vez criada a biblioteca de índice, o número de fragmentos não pode ser modificado !

O processo para adicionar novos documentos é o seguinte:
Insira a descrição da imagem aqui

Interpretação:

  • 1) Adicione um id=1novo documento
  • 2) Faça a operação idnele hash. Se o resultado for 2, ele deve ser armazenado emshard-2
  • 3) shard-2O fragmento primário está no node3nó e roteia os dados paranode3
  • 4) Salve o documento
  • 5) Sincronize a shard-2cópia fornecida replica-2no node2
  • 6) Retorne o resultado para coordinating-nodeo nó

4.4. Consulta distribuída de cluster

elasticsearchA consulta é dividida em duas etapas:

  • scatter phaseestágio de dispersão, coordinating node(nó coordenador) distribuirá a solicitação para cada fragmento
  • gather phaseestágio de agregação, coordinating noderesumir data nodeos resultados da pesquisa e processá-los em um conjunto de resultados final retornado ao usuário

Insira a descrição da imagem aqui

resumo:
Insira a descrição da imagem aqui

4.5. Failover de cluster

  Os nós no cluster mastermonitorarão o status dos nós no cluster. Se um nó estiver inativo, os dados fragmentados do nó inativo serão imediatamente migrados para outros nós para garantir a segurança dos dados. Isso é chamadofailover

1) Por exemplo, uma estrutura de cluster é mostrada na figura:
Insira a descrição da imagem aqui
Agora, node1é o nó mestre e os outros dois nós são nós escravos.

2) De repente, node1ocorre uma falha:
Insira a descrição da imagem aqui
a primeira coisa após o tempo de inatividade é reeleger o mestre. Por exemplo, se você selecionar node2:
Insira a descrição da imagem aqui
  node2Depois de se tornar o nó mestre, o status de monitoramento do cluster será verificado e será descoberto que: shard-1, shard-0não há nó de réplica. node1Portanto, os dados acima precisam ser migrados para node2:node3
Insira a descrição da imagem aqui

resumo:
Insira a descrição da imagem aqui

Acho que você gosta

Origin blog.csdn.net/weixin_52223770/article/details/128701275
Recomendado
Clasificación