分布式搜索 (二)

一、DSL 查询文档

1. DSL Query 的分类

Elasticsearch 提供了基于 JSON 的

DSL (Domain Specific  Language)

来定义查询

常见的查询类型包括:

查询所有:查询出所有数据,一般测

    试用

    例如:match_all

全文检索 (full text) 查询:利用分词器

   对用户输入内容分词,然后去倒排索引

   库中匹配。

   例如:match_query

              multi_match_query

精确查询:根据精确词条值查找数据,

   一般是查找 keyword、数值、日期、

   boolean 等类型字段。

   例如:ids、range 、term

地理 (geo) 查询:根据经纬度查询。

    例如:geo_distance

              geo_bounding_box

复合 (compound) 查询:复合查询可以

    将上述各种查询条件组合起来,合并查

    询条件

    例如:bool function_score   

查询 DSL 的基本语法:

GET /索引库名/_search

{

        "query":{

                "查询类型":{

                        "FIELD":"TEXT"

                }

        }

2. 全文检索查询

对用户输入内容分词,常用于搜索框搜索:

match 查询:全文检索查询的一种,会

    对用户输入内容分词,然后去倒排索引

    库检索,语法:

multi_match:与 match 查询类似,只

    不过允许同时查询多个字段,语法: 

 

 

3. 精准查询

精确查询一般是查找 keyword、数值、

日期、boolean 等类型字段,所以不会

对搜索条件分词

常见的有:

term:根据词条精确值查询

 

range:根据值的范围查询

 

 

4. 地理坐标查询

geo_bounding_box:查询 geo_point

    值落在某个矩形范围的所有文档

 

geo_distance:查询到指定中心点小

    于某个距离值的所有文档

 

5. 复合查询

fuction score:算分函数查询,可以控制

             文档相关性算分,控制文档排名

(1) 相关性算分

利用 match 查询时,文档结果会根据与搜

索词条的关联度打分 (_score),返回结果

时按照分值降序排列

TF-IDF:在 elasticsearch5.0 之前,

              会随着词频增加而越来越大

BM25:在 elasticsearch5.0 之后,会

            随着词频增加而增大,但增

            曲线会趋于水平

(2) Function Score Query

使用 function score query,可以修改文档

的相关性算分 (query score),根据新得到

的算分排序

function score query 定义的三要素:

过滤条件:哪些文档要加分

算分函数:如何计算 function score

加权方式:function score 与 query

                      score 如何运算 

(3) Boolean Query

布尔查询是一个或多个查询子句的组合

子查询的组合方式有:

must:必须匹配每个子查询,类似 “

should:选择性匹配子查询,类似 “

must_not:必须不匹配,不参与算分,

                  类似 “

filter:必须匹配,不参与算分 

 

二、搜索结果处理

1. 排序

es 支持对搜索结果排序,默认是根据

相关度算分 (_score) 来排序,可以排

序字段类型有:keyword 类型、数值

类型、地理坐标类型、日期类型等

 

 

2. 分页

es 默认情况下只返回 top10 的数据,

而如果要查询更多数据就需要修改分

页参数了,通过修改 from、size

数来控制要返回的分页结果

 (2) 深度分页问题

ES 是分布式的,所以会面临深度分页问题

例如:按 price 排序后,获取 from = 990,

          size =10 的数据:

① 在每个数据分片上都排序并查询前

    1000条文档

② 将所有节点的结果聚合,在内存中

    重新排序选出前1000条文档

③ 从这1000条中,选取从990开始的

    10条文档

  

如果搜索页数过深,或者结果集 (from

+ size) 越大,对内存和 CPU 的消耗也

越高,因此 ES 设定结果集查询的上限

是10000

深度分页解决方案:

search after:分页时需要排序,原

    理是从上一次的排序值开始,查询

    下一页数据 (推荐)

scroll:原理将排序数据形成快照

    保存在内存 (不推荐使用)

总结:

from + size

优点:支持随机翻页

缺点:深度分页问题,默认查询上限

          (from + size) 是10000

场景:百度、京东、谷歌、淘宝这样

          的随机翻页搜索

after search

优点:没有查询上限 (单次查询的 size

          不超过10000)

缺点:只能向后逐页查询,不支持随机

          翻页

场景:没有随机翻页需求的搜索,例如

          手机向下滚动翻页

scroll

优点:没有查询上限 (单次查询的 size

          不超过10000)

缺点:会有额外内存消耗,并且搜索

          果是非实时的

场景:海量数据的获取和迁移,从 ES

          7.1 开始不推荐

3. 高亮

在搜索结果中把搜索关键字突出显示

原理:

将搜索结果中的关键字用标签标记

来,在页面中给标签添加 css 样式

 搜索结果处理整体语法:

 

三、RestClient 查询文档

1. 快速入门

(1) 发起查询请求

request.source() 包含了查询、排序、

分页、高亮等所有功能

QueryBuilders 包含 match、term、

function_score、bool 等各种查询

(2) 解析响应 

 

es 返回的结果是一个 JSON 字符串,

结构包含:

hits:命中的结果
        total:总条数,其中的 value 是具

                 体的总条数值
        max_score:所有结果中得分最高

                             的文档的相关性算分
        hits:搜索结果的文档数组,其中

                的每个文档都是一个json对象
                _source:文档中的原始数据

                                也是json对象

所以解析响应结果,就是逐层解析 JSON 字

符串

@Test
void testMatchAll() throws IOException {// 1.准备RequestSearchRequest request = new SearchRequest("hotel");// 2.准备DSLrequest.source().query(QueryBuilders.matchAllQuery());// 3.发送请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4.解析响应handleResponse(response);
}private void handleResponse(SearchResponse response) {// 4.解析响应SearchHits searchHits = response.getHits();// 4.1.获取总条数long total = searchHits.getTotalHits().value;System.out.println("共搜索到" + total + "条数据");// 4.2.文档数组SearchHit[] hits = searchHits.getHits();// 4.3.遍历for (SearchHit hit : hits) {// 获取文档sourceString json = hit.getSourceAsString();// 反序列化HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);System.out.println("hotelDoc = " + hotelDoc);}
}

2. match 查询

与 match_all 差别是查询条件,也就是

query 的部分

Java 代码上的差异主要是 request.source().

query() 中的参数

 

@Test
void testMatch() throws IOException {// 1.准备RequestSearchRequest request = new SearchRequest("hotel");// 2.准备DSLrequest.source().query(QueryBuilders.matchQuery("all", "如家"));// 3.发送请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4.解析响应handleResponse(response);}

3. 精确查询

与之前的查询相比,差异同样在查询条件

 

 

4. 复合查询--Boolean Query

 

@Test
void testBool() throws IOException {// 1.准备RequestSearchRequest request = new SearchRequest("hotel");// 2.准备DSL// 2.1.准备BooleanQueryBoolQueryBuilder boolQuery = QueryBuilders.boolQuery();// 2.2.添加termboolQuery.must(QueryBuilders.termQuery("city", "杭州"));// 2.3.添加rangeboolQuery.filter(QueryBuilders.rangeQuery("price").lte(250));request.source().query(boolQuery);// 3.发送请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4.解析响应handleResponse(response);}

5. 排序、分页

搜索结果的排序和分页是与 query

的参数,因此同样是使用 request.

source() 来设置

 

@Test
void testPageAndSort() throws IOException {// 页码,每页大小int page = 1, size = 5;// 1.准备RequestSearchRequest request = new SearchRequest("hotel");// 2.准备DSL// 2.1.queryrequest.source().query(QueryBuilders.matchAllQuery());// 2.2.排序 sortrequest.source().sort("price", SortOrder.ASC);// 2.3.分页 from、sizerequest.source().from((page - 1) * size).size(5);// 3.发送请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4.解析响应handleResponse(response);}

6. 高亮

与之前代码差异较大:

① 查询的DSL:其中除了查询条件,

    需要添加高亮条件,同样是与 query

    同级

② 结果解析:结果除了要解析 _source

    文档数据,还要解析高亮结果

(1) 请求的 DSL 构建 

 

@Test
void testHighlight() throws IOException {// 1.准备RequestSearchRequest request = new SearchRequest("hotel");// 2.准备DSL// 2.1.queryrequest.source().query(QueryBuilders.matchQuery("all","如家"));// 2.2.高亮request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));// 3.发送请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4.解析响应handleResponse(response);}

 (2) 结果解析

 

private void handleResponse(SearchResponse response) {// 4.解析响应SearchHits searchHits = response.getHits();// 4.1.获取总条数long total = searchHits.getTotalHits().value;System.out.println("共搜索到" + total + "条数据");// 4.2.文档数组SearchHit[] hits = searchHits.getHits();// 4.3.遍历for (SearchHit hit : hits) {// 获取文档sourceString json = hit.getSourceAsString();// 反序列化HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);// 获取高亮结果Map<String, HighlightField> highlightFields = hit.getHighlightFields();if (!CollectionUtils.isEmpty(highlightFields)) {// 根据字段名获取高亮结果HighlightField highlightField = highlightFields.get("name");if (highlightField != null) {// 获取高亮值String name = highlightField.getFragments()[0].string();// 覆盖非高亮结果hotelDoc.setName(name);}}System.out.println("hotelDoc = " + hotelDoc);}
}

四、数据聚合

1. 聚合的种类

聚合 (aggregations) 可以实现对文档

数据的统计、分析、运算,参与聚合

的字段类型必须是:keyword、数值、

日期、布尔

聚合常见的有三类:

聚合 (Bucket):用来对文档做分组

     TermAggregation:按照文档字段值

                                   分组

     Date Histogram:按照日期阶梯分组,

         例如一周为一组,或者一月为一组

度量聚合 (Metric):用以计算一些值,

     Avg:求平均值         

     Max:求最大值

     Min:求最小值

     Stats:同时求 max、min、avg、sum 等

管道聚合 (pipeline):其它聚合的结果

                                      为基础做聚合

2. DSL 实现聚合

(1) DSL 实现 Bucket 聚合

聚合必须的三要素:

① 聚合名称

② 聚合类型

③ 聚合字段

  2) 聚合结果排序

默认情况下,Bucket 聚合会统计 Bucket

内的文档数量,记为 _count,并且按照

_count 降序排序

可以修改结果排序方式:

 

   3) 限定聚合范围

默认情况下,Bucket 聚合是对索引库

所有文档做聚合,我们可以限定

聚合的文档范围,只要添加 query 条

件即可

 (2) DSL 实现 Metrics 聚合

 

3. RestAPI 实现聚合

  2) 聚合结果解析 

@Test 
Void testAggregation() throws IOException {// 1. 准备 RequestSearchRequest request = new SearchRequest("hotel");// 2. 准备 DSL// 2.1 设置 sizerequest.source().size(0);// 2.2 聚合request.source().aggregation(AggregationBuilders.terms("brandAgg").field("brand").size(10));// 3. 发出请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4. 解析结果Aggregations aggregations = response.getAggregations();// 4.1 根据聚合名称获取聚合结果Terms brandTerms = aggregations.get("brandAgg");// 4.2 获取 bucketsList<? extends Terms.Bucket> buckets = brandTerms.getBuckets();// 4.3 遍历for (Terms.Bucket bucket : buckets) {//4.4 获取 keyString key = bucket.getKeyAsString();System.out.println(key);}
}

 

五、自动补全

1. 拼音分词器

要实现根据字母做补全,就必须对文档

按照拼音分词,需要安装拼音分词插件

GitHub - medcl/elasticsearch-analysis-pinyin: This Pinyin Analysis plugin is used to do conversion between Chinese characters and Pinyin.

2. 自定义分词器

es 中分词器 (analyzer) 的组成包含三部分:

character filters:在 tokenizer 之前

   对文本进行处理,例如删除字符、替

   换字符

tokenizer:将文本按照一定的规则切

   割成词条 (term),例如 keyword,就

   是不分词;还有ik_smart

tokenizer filter:将 tokenizer 输出的

   词条做进一步处理,例如大小写转换、

   同义词处理、拼音处理等

在创建索引库时,通过 settings 来配置自定

义的 analyzer(分词器):

 

 

// 自定义拼音分词器
PUT /test
{"settings": {"analysis": {"analyzer": { "my_analyzer": { "tokenizer": "ik_max_word","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}}}}
}

① 拼音分词器适合在创建倒排索引

的时候使用,但不能在搜索的时候

使用

② 因此字段在创建倒排索引时应该

用 my_analyzer 分词器,字段在搜

索时应该使用 ik_smart 分词器

 

3. 自动补全查询

es 中使用 completion suggester 查询

来实现自动补全功能,匹配以用户输

入内容开头的词条并返回

为了提高补全查询的效率,对于文档中字

段的类型有一些约束:

① 参与的字段必须是 completion 类型

② 字段的内容一般是用来补全的多个

    词条形成的数组

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

4. RestAPI 实现自动补全

@Test 
void testSuggest() throws IOEvxception {// 1. 准备 RequestSearchRequest request = new SearchRequest("hotel");// 2. 准备 DSLrequest.source().suggest(new SuggestBuilder().addSuggestion("suggestions"SuggestBuilders.completionSuggestion("suggestion").prefix("h").skipDuplicates(true).size(10)));// 3. 发出请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4. 处理结果Suggest suggest = response.getSuggest();// 4.1 根据名称获取补全结果CompletionSuggestion suggestion = suggest.getSuggestion("hotelSuggestion");// 4.2 获取 options 并遍历for (CompletionSuggestion.Entry.Option option : suggestion.getOptions()) {//4.3 获取一个 option 中的 text,也就是补全的词条String text = option.getText().string();System.out.println(text);}
}

六、数据同步

数据同步:es 中的数据来自于 mysql

                  数据库,因此 mysql 数据

                  发生改变时,es 也必须跟

                  着改变

跨微服务的数据同步问题解决方案

同步调用

    优点:实现简单,粗暴

    缺点:业务耦合度高

异步通知

    优点:低耦合,实现难度一般

    缺点:依赖 mq 的可靠性

监听 binlog

    优点:完全解除服务间耦合

    缺点:开启 binlog 增加数据库负担、

              实现复杂度高

七、集群

单机的 es 做数据存储,必然面临两个问

题,海量数据存储问题和单点故障问题

解决方案:

海量数据存储问题:将索引库从逻辑上

    拆分为N个分片(shard),存储到多个节

    点

单点故障问题:将分片数据在不同节点

    备份(replica)

1. 搭建 ES 集群

  (1) 创建 es 集群

   1) 编写一个 docker-compose 文件,内容如下:

version: '2.2'
services:es01:image: docker.elastic.co/elasticsearch/elasticsearch:7.12.1container_name: es01environment:- node.name=es01- cluster.name=es-docker-cluster- discovery.seed_hosts=es02,es03- cluster.initial_master_nodes=es01,es02,es03- bootstrap.memory_lock=true- "ES_JAVA_OPTS=-Xms512m -Xmx512m"ulimits:memlock:soft: -1hard: -1volumes:- data01:/usr/share/elasticsearch/dataports:- 9200:9200networks:- elastices02:image: docker.elastic.co/elasticsearch/elasticsearch:7.12.1container_name: es02environment:- node.name=es02- cluster.name=es-docker-cluster- discovery.seed_hosts=es01,es03- cluster.initial_master_nodes=es01,es02,es03- bootstrap.memory_lock=true- "ES_JAVA_OPTS=-Xms512m -Xmx512m"ulimits:memlock:soft: -1hard: -1volumes:- data02:/usr/share/elasticsearch/datanetworks:- elastices03:image: docker.elastic.co/elasticsearch/elasticsearch:7.12.1container_name: es03environment:- node.name=es03- cluster.name=es-docker-cluster- discovery.seed_hosts=es01,es02- cluster.initial_master_nodes=es01,es02,es03- bootstrap.memory_lock=true- "ES_JAVA_OPTS=-Xms512m -Xmx512m"ulimits:memlock:soft: -1hard: -1volumes:- data03:/usr/share/elasticsearch/datanetworks:- elasticvolumes:data01:driver: localdata02:driver: localdata03:driver: localnetworks:elastic:driver: bridge

  2) es 运行需要修改一些 linux 系统权限,

      修改 /etc/sysctl.conf 文件

① 添加 vm.max_map_count=262144

② 执行命令 sysctl -p

  3) 通过 docker-compose 文件启动集群

docker-compose up -d

(2) 集群状态监控

使用 cerebro 来监控 es 集群状态

GitHub - lmenezes/cerebro

(3) 创建索引库

方法一:利用 kibana 的 DevTools 创建索引库

PUT /itcast
{"settings": {"number_of_shards": 3, // 分片数量"number_of_replicas": 1 // 副本数量},"mappings": {"properties": {// mapping映射定义 ...}}
}

方法二:利用 cerebro 创建索引库

 

4) 查看分片效果

回到首页,即可查看索引库分片效果:

  

2. 集群脑裂问题

(1) ES 集群的节点角色

(2) ES 集群的分布式查询

es 中的每个节点角色都有自己不同

的职责,因此建议集群部署时,每

个节点都有独立的角色 

各节点的作用: 

aster eligible:参与集群选主;主节点可

    以管理集群状态、管理分片信息、处理创

    建和删除索引库的请求

data:数据的 CRUD

coordinator:路由请求到其它节点;合并

                         查询到的结果,返回给用户 

 (3) ES 集群的脑裂

默认情况下,每个节点都是 master eligible

节点,因此一旦 master 节点宕机,其它候

选节点会选举一个成为主节点,当主节点

与其他节点网络故障时,可能发生脑裂问题

为了避免脑裂,需要要求选票超过 (eligible

节点数量 + 1) / 2 才能当选为主,因此

eligible 节点数量最好是奇数,对应配置项

discovery.zen.minimum_master_nodes

在 es7.0 以后,已经成为默认配置,因此一

般不会发生脑裂问题

3. 集群分布式存储

新增文档时,应该保存到不同分片,保证

数据均衡

coordinating node 通过 hash 算法计算文档应该

存储到哪个分片:

shard = hash(_routing) % number_of_shards

① _routing 默认是文档的 id

② 算法与分片数量有关,因此索引库一旦

    创建,分片数量不能修改

 

4. 集群分布式查询

es 查询分成两个阶段:

scatter phase分散阶段,coordinating

    node 会把请求分发到每一个分片

gather phase聚集阶段,coordinating

    node 汇总 data node 的搜索结果,并处

    理为最终结果集返回给用户

5. 集群故障转移

集群的 master 节点会监控集群中的节点状

态,如果发现有节点宕机,会立即将宕机节

点的分片数据迁移到其它节点,确保数据安

全,这个叫做故障转移

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/21784.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

这么看,项目经理根本不可能失业

早上好&#xff0c;我是老原。 不知道做项目经理的朋友们有没有这种感觉&#xff0c;明明项目经理是一个高大上的管理岗位&#xff0c;但为何总觉得自己的工作是一个打杂的&#xff1f; 最近就有一个粉丝朋友来和我吐槽&#xff1a;明明是升职&#xff0c;为啥感觉被坑了。 …

基于 MNN 在个人设备上流畅运行大语言模型

LLM&#xff08;大语言模型&#xff09;因其强大的语言理解能力赢得了众多用户的青睐&#xff0c;但LLM庞大规模的参数导致其部署条件苛刻&#xff1b;在网络受限&#xff0c;计算资源有限的场景下无法使用大语言模型的能力&#xff1b;低算力&#xff0c;本地化部署的问题亟待…

【测试开发】自动化测试 selenium 篇

目录 一. 什么是自动化测试 二. selenium 1. selenium的工作原理 2. seleniumJava的环境搭建(Chrome浏览器) 三. selenium中常用的API 1. 定位元素 findElement 1.1 css选择语法 1.2 xpath 2. 操作测试对象 2.1 sendKeys-在对象上模拟按键输入 2.2 click-点击对象…

IP 协议的相关特性

目录 IP协议有三大特点&#xff1a;无连接、无状态、不可靠。 四位版本号 四位头部长度 八位服务类型: 十六位总长度 16 位标识, 3 位标志, 13 位片偏移 八位生存时间 八位协议 十六位首部校验和 关于IP v4地址不够的问题 ip地址动态分配: ip地址转换(NAT) 数据传输…

【xxl-job】分布式任务调度系统xxl-job搭建

XXL-JOB是一个轻量级分布式任务调度平台&#xff0c;其核心设计目标是开发迅速、学习简单、轻量级、易扩展、开箱即用。 更多介绍&#xff0c;请访问官网&#xff1a;分布式任务调度平台XXL-JOB 一、任务调度中心(基于docker)【Version 2.4.0】 前提条件&#xff1a;任务调度…

复习Javascript数组

JavaScript 数组 JS 数字属性JS 数组方法 JavaScript 数组用于在单一变量中存储多个值。 var cars ["Saab", "Volvo", "BMW"]; 什么是数组&#xff1f; 数组是一种特殊的变量&#xff0c;它能够一次存放一个以上的值。 如果您有一个项目清…

MySQL表单查询以及多表查询

1.单表查询 CREATE TABLE emp ( empno int(4) NOT NULL, ename varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, job varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, mgr int(4) NULL DEFAULT NULL, hi…

IOP JPCS独立出版 | 2023年第二届材料科学与工程国际会议(CoMSE 2023)

会议简介 Brief Introduction 2023年第二届材料科学与工程国际会议(CoMSE 2023) 会议时间&#xff1a;2023年7月21日-23日 召开地点&#xff1a;中国泰州 大会官网&#xff1a;www.icomse.org CoMSE 2023由四川大学、华南理工大学亚热带建筑科学国家重点实验室、国际电气电子与…

Raft算法之日志复制

Raft算法之日志复制 一、日志复制大致流程 在Leader选举过程中&#xff0c;集群最终会选举出一个Leader节点&#xff0c;而集群中剩余的其他节点将会成为Follower节点。Leader节点除了向Follower节点发送心跳消息&#xff0c;还会处理客户端的请求&#xff0c;并将客户端的更…

如何在 Windows 中免费合并 PDF 文件 [在线和离线]

PDF是一种广泛使用的文件格式&#xff0c;具有兼容性好、安全性高、易于打印、方便浏览等众多优点。在工作和学习过程中&#xff0c;经常需要将同一类型的PDF文件合并起来&#xff0c;以方便传输和查看&#xff0c;使得合并PDF文件成为一种重要的数据整合方法。 如果您想知道如…

android更换开机动画

android11 路径&#xff1a;device / {vendor-name} / {platform-name} / {device-name} / system / bootanimation.zip 例&#xff1a;android \ device \ softwinner \ ceres \ ceres-b6 \ system \ bootanimation.zip android13 路径&#xff1a;device / softwinner / {PRO…

基于单片机空气质量检测二氧化碳 一氧化碳温湿度PM2.5检测系统的设计与实现

功能介绍 以51单片机作为主控系统&#xff1b;对空气空气中有毒有害气体进行监测&#xff1b;使用LCD1602液晶显示&#xff0c;采集到的PM2.5值通过单片机串口传输&#xff1b;通过传感器对室内PM2.5粉尘进行检查&#xff1b;通过按键设置的上限值&#xff1b;当检测到有毒气体…