elasticsearch之七search搜索详解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了elasticsearch之七search搜索详解相关的知识,希望对你有一定的参考价值。

参考技术A

query phase

一次请求要打到所有shard的一个replica/primary上去,如果每个shard都有多个replica,那么同时并发过来的搜索请求可以同时打到其他的replica上去

search的参数都是类似http请求头中的字符串参数提供搜索条件的。

GET [/index_name/type_name/]_search[?parameter_name=parameter_value&...]

解释

took:耗费了几毫秒

timed_out:是否超时,这里是没有

_shards:到几个分片搜索,成功几个,跳过几个,失败几个。

hits.total:查询结果的数量,3个document

hits.max_score:score的含义,就是document对于一个search的相关度的匹配分数,越相关,就越匹配,分数也高

hits.hits:包含了匹配搜索的document的所有详细数据

与http请求传参类似

类比sql: select * from book where name like ’ %java%’ order by price desc

timeout参数:是超时时长定义。代表每个节点上的每个shard执行搜索时最多耗时多久。不会影响响应的正常返回。只会影响返回响应中的数据数量。

如:索引a中,有10亿数据。存储在5个shard中,假设每个shard中2亿数据,执行全数据搜索的时候,需要耗时1000毫秒。定义timeout为10毫秒,代表的是shard执行10毫秒,搜索出多少数据,直接返回。

GET /book/_search?timeout=10ms

全局设置:配置文件中设置 search.default_search_timeout:100ms。默认不超时。

所谓的multi-index就是从多个index中搜索数据。相对使用较少,只有在复合数据搜索的时候,可能出现。一般来说,如果真使用复合数据搜索,都会使用_all。

应用场景:生产环境log索引可以按照日期分开。

log_to_es_20190910

log_to_es_20190911

log_to_es_20180910

默认情况下,Elasticsearch搜索返回结果是10条数据。从第0条开始查询。

+/-搜索

+ :和不定义符号含义一样,就是搜索指定的字段中包含key words的数据

- : 与+符号含义相反,就是搜索指定的字段中不包含key words的数据

根据相关度评分倒排序,所以分页过深,协调节点会将大量数据聚合分析。

GET /book/_search?q=name:java

GET /book/_search?q=+name:java

GET /book/_search?q=-name:java

直接可以搜索所有的field,任意一个field包含指定的关键字就可以搜索出来。我们在进行中搜索的时候,难道是对document中的每一个field都进行一次搜索吗?不是的。

es中_all元数据。建立索引的时候,插入一条docunment,es会将所有的field值经行全量分词,把这些分词,放到_all field中。在搜索的时候,没有指定field,就在_all搜索。

举例

_all : jack,123@qq.com ,beijing 作为这一条document的_all field的值,同时进行分词后建立对应的倒排索引

DSL - Domain Specified Language , 特殊领域的语言。

请求参数是请求体传递的。在Elasticsearch中,请求体的字符集默认为UTF-8。

query string 后边的参数原来越多,搜索条件越来越复杂,不能满足需求。

DSL:Domain Specified Language,特定领域的语言

es特有的搜索语言,可在请求体中携带搜索条件,功能强大。

查询全部 GET /book/_search

排序 GET /book/_search?sort=price:desc

分页查询 GET /book/_search?size=10&from=0

指定返回字段 GET /book/ _search? _source=name,studymodel

通过组合以上各种类型查询,实现复杂查询。

搜索需求:title必须包含elasticsearch,content可以包含elasticsearch也可以不包含,author_id必须不为111

初始数据:

搜索:

返回:

更复杂的搜索需求:

select * from test_index where name=\'tom\' or (hired =true and (personality =\'good\' and rude != true ))

重新创建book索引

插入数据

搜索

relevance score算法,简单来说,就是计算出,一个索引中的文本,与搜索文本,他们之间的关联匹配程度。

Elasticsearch使用的是 term frequency/inverse document frequency算法,简称为TF/IDF算法。TF词频(Term Frequency),IDF逆向文件频率(Inverse Document Frequency)

Term frequency :搜索文本中的各个词条在field文本中出现了多少次,出现次数越多,就越相关。

举例:搜索请求:hello world

doc1 : hello you and me,and world is very good.

doc2 : hello,how are you

Inverse document frequency :搜索文本中的各个词条在整个索引的所有文档中出现了多少次,出现的次数越多,就越不相关.

举例:搜索请求:hello world

doc1 : hello ,today is very good

doc2 : hi world ,how are you

整个index中1亿条数据。hello的document 1000个,有world的document 有100个。

doc2 更相关

Field-length norm :field长度,field越长,相关度越弱

举例:搜索请求:hello world

doc1 : "title":"hello article","content ":"balabalabal 1万个"

doc2 : "title":"my article","content ":"balabalabal 1万个,world"

返回

搜索的时候,要依靠倒排索引;排序的时候,需要依靠正排索引,看到每个document的每个field,然后进行排序,所谓的正排索引,其实就是doc values

在建立索引的时候,一方面会建立倒排索引,以供搜索用;一方面会建立正排索引,也就是doc values,以供排序,聚合,过滤等操作使用

doc values是被保存在磁盘上的,此时如果内存足够,os会自动将其缓存在内存中,性能还是会很高;如果内存不足够,os会将其写入磁盘上

倒排索引

doc1: hello world you and me

doc2: hi, world, how are you

搜索时:

hello you --> hello, you

hello --> doc1

you --> doc1,doc2

doc1: hello world you and me

doc2: hi, world, how are you

sort by 出现问题

正排索引

doc1: "name": "jack", "age": 27

doc2: "name": "tom", "age": 30

一般搜索,如果不加from和size,就默认搜索前10条,按照_score排序

短语检索。要求查询条件必须和具体数据完全匹配才算搜索结果。其特征是:1-搜索条件不做任何分词解析;2-在搜索字段对应的倒排索引(正排索引)中进行精确匹配,不再是简单的全文检索。

决定了哪些shard会被用来执行搜索操作

_primary, _primary_first, _local, _only_node:xyz, _prefer_node:xyz, _shards:2,3

bouncing results问题,两个document排序,field值相同;不同的shard上,可能排序不同;每次请求轮询打到不同的replica shard上;每次页面上看到的搜索结果的排序都不一样。这就是bouncing result,也就是跳跃的结果。

搜索的时候,是轮询将搜索请求发送到每一个replica shard(primary shard),但是在不同的shard上,可能document的排序不同

解决方案就是将preference设置为一个字符串,比如说user_id,让每个user每次搜索的时候,都使用同一个replica shard去执行,就不会看到bouncing results了

主要就是限定在一定时间内,将部分获取到的数据直接返回,避免查询耗时过长

document文档路由,_id路由,routing=user_id,这样的话可以让同一个user对应的数据到一个shard上去

default:query_then_fetch

dfs_query_then_fetch,可以提升revelance sort精准度

ElasticSearch查询流程详解

参考技术A

前面已经介绍了ElasticSearch的写入流程,了解了ElasticSearch写入时的分布式特性的相关原理。ElasticSearch作为一款具有强大搜索功能的存储引擎,它的读取是什么样的呢?读取相比写入简单的多,但是在使用过程中有哪些需要我们注意的呢?本篇文章会进行详细的分析。

在前面的文章我们已经知道ElasticSearch的读取分为两种GET和SEARCH。这两种操作是有一定的差异的,下面我们先对这两种核心的数据读取方式进行一一分析。

(图片来自官网)

以下是从主分片或者副本分片检索文档的步骤顺序:

注意:

在协调节点有个http_server_worker线程池。收到读请求后它的具体过程为:

数据节点上有一个get线程池。收到了请求后,处理过程为:

注意:

get过程会加读锁。处理realtime选项,如果为true,则先判断是否有数据可以刷盘,然后调用Searcher进行读取。Searcher是对IndexSearcher的封装在早期realtime为true则会从tranlog中读取,后面只会从index的lucene读取了。即实时的数据只在lucene之中。

对于Search类请求,ElasticSearch请求是查询lucene的Segment,前面的写入详情流程也分析了,新增的文档会定时的refresh到磁盘中,所以搜索是属于近实时的。而且因为没有文档id,你不知道你要检索的文档在哪个分配上,需要将索引的所有的分片都去搜索下,然后汇总。ElasticSearch的search一般有两个搜索类型

所有的搜索系统一般都是两阶段查询:

第一阶段查询到匹配的docID,第二阶段再查询DocID对应的完整文档。这种在ElasticSearch中称为query_then_fetch,另一种就是一阶段查询的时候就返回完整Doc,在ElasticSearch中叫query_and_fetch,一般第二种适用于只需要查询一个Shard的请求。因为这种一次请求就能将数据请求到,减少交互次数,二阶段的原因是需要多个分片聚合汇总,如果数据量太大那么会影响网络传输效率,所以第一阶段会先返回id。

除了上述的这两种查询外,还有一种三阶段查询的情况。

搜索里面有一种算分逻辑是根据TF和DF来计算score的,而在普通的查询中,第一阶段去每个Shard中独立查询时携带条件算分都是独立的,即Shard中的TF和DF也是独立的。虽然从统计学的基础上数据量多的情况下,每一个分片的TF和DF在整体上会趋向于准确。但是总会有情况导致局部的TF和DF不准的情况出现。

ElasticSearch为了解决这个问题引入了DFS查询。

比如DFS_query_then_fetch,它在每次查询时会先收集所有Shard中的TF和DF值,然后将这些值带入请求中,再次执行query_then_fetch,这样算分的时候TF和DF就是准确的,类似的有DFS_query_and_fetch。这种查询的优势是算分更加精准,但是效率会变差。

另一种选择是用BM25代替TF/DF模型。

在ElasticSearch7.x,用户没法指定以下两种方式: DFS_query_and_fetch query_and_fetch

注:这两种算分的算法模型在《ElasticSearch实战篇》有介绍:

这里query_then_fetch具体的搜索的流程图如下:

(图片来自官网)

查询阶段包含以下四个步骤:

以上就是ElasticSearch的search的详细流程,下面会对每一步进行进一步的说明。

协调节点处理query请求的线程池为:

http_server_work

负责该解析功能的类为:

org.elasticsearch.rest.action.search.RestSearchAction

主要将restquest的参数封装成SearchRequest

这样SearchRequest请求发送给TransportSearchAction处理

将索引涉及到的shard列表或者有跨集群访问相关的shard列表合并

如果有多个分片位于同一个节点,仍然会发送多次请求

shardsIts为搜索涉及的所有分片,而shardRoutings.nextOrNull()会从分片的所有副本分片选出一个分片来请求。

onShardSuccess对收集到的结果进行合并,这里需要检查所有的请求是否都已经有了回复。

然后才会判断要不要进行executeNextPhase

当返回结果的分片数等于预期的总分片数时,协调节点会进入当前Phase的结束处理,启动下一个阶段Fetch Phase的执行。onPhaseDone()会executeNextPhase来执行下一个阶段。

当触发了executeNextPhase方法将触发fetch阶段

上一步的executeNextPhase方法触发Fetch阶段,Fetch阶段的起点为FetchSearchPhase#innerRun函数,从查询阶段的shard列表中遍历,跳过查询结果为空的 shard。其中也会封装一些分页信息的数据。

使用了countDown多线程工具,fetchResults存储某个分片的结果,每收到一个shard的数据就countDoun一下,当都完毕后,触发finishPhase。接着会进行下一步:

CountedCollector:

finishPhase:

执行字段折叠功能,有兴趣可以研究下。即ExpandSearchPhase模块。ES 5.3版本以后支持的Field Collapsing查询。通过该类查询可以轻松实现按Field值进行分类,每个分类获取排名前N的文档。如在菜单行为日志中按菜单名称(用户管理、角色管理等)分类,获取每个菜单排名点击数前十的员工。用户也可以按Field进行Aggregation实现类似功能,但Field Collapsing会更易用、高效。

ExpandSearchPhase执行完了,就返回给客户端结果了。

处理数据节点请求的线程池为:search

根据前面的两个阶段,数据节点主要处理协调节点的两类请求:query和fetch

这里响应的请求就是第一阶段的query请求

executeQueryPhase:

executeQueryPhase会执行loadOrExecuteQueryPhase方法

这里判断是否从缓存查询,默认启用缓存,缓存的算法默认为LRU,即删除最近最少使用的数据。如果不启用缓存则会执行queryPhase.execute(context);底层调用lucene进行检索,并且进行聚合。

关键点:

ElasticSearch查询分为两类,一类为GET,另一类为SEARCH。它们使用场景不同。

本文主要分析了ElasticSearch分布式查询主体流程,并未对lucene部分进行分析,有兴趣的可以自行查找相关资料。

以上是关于elasticsearch之七search搜索详解的主要内容,如果未能解决你的问题,请参考以下文章

Elasticsearch: 使用URI Search

elasticsearch

Elasticsearch——search搜索入门

Elasticsearch跨集群搜索(Cross Cluster Search)

SharePoint 中search的原理!!!!

Elasticsearch 异步搜索 Async search 实战