在ES执行分布式搜索时,分布式搜索操作需要分散到所有相关分片,若一个索引有3个主分片,每个主分片有一个副本分片,那么搜索请求会在这6个分片中随机选择3个分片,这3个分片有可能是主分片也可能是副本分片,然后收集所有分片的查询结果。所以ES的搜索过程分为两个阶段,Query阶段和Fetch阶段;ES有两种搜索类型:query_then_fetch,dfs_query_then_fetch。

  1.Query阶段

  1)转发请求。在Query阶段客户端向ES节点发送,搜索请求,Coordinate节点接受客户端搜索请求,Coordinate节点负责解析搜索请求,并在索引的所有主副本分片中随机选择分片,并且发送给分片所在的数据节点。

  2)执行查询。接收到查询请求的数据节点执行查询操作,并对查询结果进行排序,每个节点都会根据请求中参数返回from+size个排序后的文档Id和排序值给Coordinate节点。

  2.Fetch阶段

  1)重排序。Coordinate节点收到数据节点返回的数据后,会按照返回的排序值对从所有分片取回的值重新进行排序,最终只选取客户端需要的from+size个文档的Id。

  2)获取文档数据。Coordinate节点根据选取的文档的Id,到相应的分片获取详细的文档数据,最终将查询到的结果返回给客户端。

查询结果解读:

  1. {
  2. "took":3, 查询所用的毫秒数
  3. "timed_out":false, 是否有分片超时,即是否只返回了部分结果
  4. "_shards":{
  5. "total":1, 一共查询了多少分片
  6. "successful":1, 多少分片成功返回
  7. "skipped":0,跳过了多少分片
  8. "failed":0  多少分片查询失败
  9. },
  10. "hits":{  
  11. "total":{
  12. "value":1, 该搜索请求中返回的所有匹配的数量
  13. "relation":"eq" 文档与搜索值的关系,eq表示相等
  14. },
  15. "max_score":8.044733, 返回结果中文档的最大得分
  16. "hits":[  查询结果的文档数组
  17. {
  18. "_index":"kibana_sample_data_ecommerce", 查询的索引
  19. "_type":"_doc",  查询的类型
  20. "_id":"4X-j7XEB-r_IFm6PISqV", 返回文档的主键
  21. "_score":8.044733,  返回文档的评分
  22. "_source":{ 文档的原始内容
  23. "currency":"EUR",
  24. "customer_first_name":"Eddie",
  25. "customer_full_name":"Eddie Underwood",
  26. "customer_gender":"MALE"
  27. ......
  28. }
  29. }
  30. ]
  31. }
  32. }

 Query Then Fetch潜在的问题

1.深度分页

  ES索引数据分布在多个分片上,在查询时,每个分片都要查询from+size个文档,Coordinate节点会聚合所有的结果,所以Coordinate节点要处理查询分片数*(from+size)个文档记录,对这些记录进行重新排序,需要的size个文档,from+size的值越大占用内存越多,称为深度分页问题,ES默认限制分页的深度不能超过10000条,可通过max_result_window设置。

  深度分页解决办法:

  1)Search After

  可以使用Search After避免深度分页的性能问题,实时获取下一页的文档信息,search_after根据上一页最后一个文档的sort值来查询下一页,并且当索引数据有变化时,也可以同步被查到,是一个实时查询的方法。

  例:http://127.0.0.1:9200/kibana_sample_data_ecommerce/_search

    查询参数:在使用Search_After查询时,第一步查询时需要指定sort字段,并且该sort字段的排序结果是唯一的,建议使用_id来进行sort,可以指定多个sort字段。

  1. {
  2. "size": 1,
  3. "query": {
  4. "match": {
  5. "currency": "EUR"
  6. }
  7. },
  8. "sort": [
  9. {
  10. "order_id": {
  11. "order": "asc"
  12. }
  13. }
  14. ]
  15. }

   返回中可以看到第一页查询返回的sort值,查询下一页时使用该sort值进行文档的定位,而后每个查询都会返回一个sort值,供下一页进行定位使用。

  1. "sort": [
  2. "550375"
  3. ]

  下一页查询:

  1. {
  2. "size": 1,
  3. "query": {
  4. "match": {
  5. "currency": "EUR"
  6. }
  7. },
  8. "search_after": [
  9. 550375
  10. ],
  11. "sort": [
  12. {
  13. "order_id": {
  14. "order": "asc"
  15. }
  16. }
  17. ]
  18. }

  Search_After存在的限制:

    a.不能指定from值,即不能想翻到哪一页就直接跳转到那一页,只能一页一页按照顺序翻;

    b.只能往后翻页,不能往前翻页。

  2)Scroll API

  scroll api可以用于从单个搜索请求中检索大量的结果,其原理是建立索引在某个时间点的快照,当快照建立后,之后的每次搜索都会在该快照上进行,对索引的所有新增操作都会被忽略,索引Scroll适合于处理大量数据,但是不能保证数据的实时性。

  POST http://127.0.0.1:9200/kibana_sample_data_ecommerce/_search?scroll=1m

  首次查询时指定scroll=5m,表示当前搜索过期时间为5分钟,即查询结果在搜到下一次请求之前会保存多次时间,scroll的值不需要长到把整个快照的数据都处理完,只需保证下一次搜索请求到来之前能处理完前一批查询结果即可。

  1. {
  2. "size": 2,
  3. "query": {
  4. "match" : {
  5. "currency" : "EUR"
  6. }
  7. }
  8. }

  返回中可以看到_scroll_id,total.value,scroll_id用于获取下一批的查询结果,total.value表示该查询有总共多少个结果。

  1. {
  2.   "_scroll_id":"DXF1ZXJ5QW5kRmV0Y2gBAAAAAAABAGUWdks0dUtFMHZTYmE1Rl9ucGp5X0hoUQ==",
  3.   "took": 1,
  4. "timed_out": false,
  5. "_shards": {
  6. "total": 1,
  7. "successful": 1,
  8. "skipped": 0,
  9. "failed": 0
  10. },
  11. "hits": {
  12. "total": {
  13. "value": 4675,
  14. "relation": "eq"
  15. },
  16. }
  17. }

  下一页:

  http://127.0.0.1:9200/_search/scroll

  下一页查询的时候不用指定索引和查询参数,只需要指定scroll时间和上一次请求返回的scroll_id,因为快照已经建好,只需要在快照上往下翻页即可。每次执行该请求都会往下进行翻页,直到查询的结果为空。

  1. {
  2. "scroll":"5m",
  3. "scroll_id":"DXF1ZXJ5QW5kRmV0Y2gBAAAAAAABAGUWdks0dUtFMHZTYmE1Rl9ucGp5X0hoUQ=="
  4. }

  Scroll API存在的限制:当快照建立后,对索引有新的操作时,无法被查询到,所以不适合做实时查询。

不同查询的使用场景

  一般查询:需要获取顶部的部分文档,查询索引最新的数据。

  全量查询:使用scroll,当需要导出全部数据,且对数据的实时性要求不高时。

  分页查询:使用from+size,当from+size过大时,使用search after。

2.相关度评分不准问题

  当搜索请求在多个shard进行数据查找时,每个分片都会基于自己分片上的文档数据进行相关度的计算,计算方法为TD/IDF,

  TF:词频,表示词条在一个文档中出现的频率;IDF:逆文档频率,log(全本文档数/词条在所有文档中出现的次数),表示该term在所有文档中出现的频率;如果查询词条在某一个文档中出现的频率(即TF)高,在全部文档中出现的频率低(即IDF)低,则表明该文档的相关性高。

  每个分片计算IDF的时候只会基于自己分片上的数据进行计算,并不会包含其他分片上的数据,所以这样会导致相关性评分不准的情况;特别在文档总数很少情况下,主分片数越多,相关性算分会越不准。

  解决相关度评分不准问题的方法:

  1)合理设置分片数量,保证数据均匀分布。

   当数据量不大时,可以考虑仅设置一个主分数;当数据量较大时,保证文档均匀的分布在各个分片上。ES提供了routing_partition_size参数,routing_partition_size越大,数据的分布越均匀(【Elasticsearch学习】之一图读懂文档索引全过程 中有提及)。

  2)使用dfs_query_then_fetch

  在搜索时,指定搜索的类型search_type=dfs_query_the_fetch,在搜索的时候,每个分片会把每个分片的TF和IDF进行搜集,然后综合所有的数据进行一次完整的相关性评分计算,但是一般不推荐,因为这样会耗费较多的CPU和内存。

 

【Elasticsearch学习】文档搜索全过程的更多相关文章

  1. ElasticSearch学习文档2018.11

    1       Elasticsearch安装 1.1    ES6.0版本安装head插件 1.1 下载head插件 下载地址:https://github.com/mobz/elasticsear ...

  2. ElasticSearch(2)-文档

    上一篇 ES(1) 官网原地址:https://www.elastic.co/guide/en/elasticsearch/reference/1.7/_cluster_health.html ES权 ...

  3. elasticsearch 路由文档到分片

    路由文档到分片 当你索引一个文档,它被存储在单独一个主分片上.Elasticsearch是如何知道文档属于哪个分片的呢?当你创建一个新文档,它是如何知道是应该存储在分片1还是分片2上的呢? 进程不能是 ...

  4. Ext JS 6学习文档-第5章-表格组件(grid)

    Ext JS 6学习文档-第5章-表格组件(grid) 使用 Grid 本章将探索 Ext JS 的高级组件 grid .还将使用它帮助读者建立一个功能齐全的公司目录.本章介绍下列几点主题: 基本的 ...

  5. ElasticSearch——原始文档和倒排索引

    一.原始文档 如上图所示, 第二象限是一份原始文档,有title和content2个字段,字段取值分别为”我是中国人”和” 热爱共X产党”,这一点没什么可解释的.我们把原始文档写入Elasticsea ...

  6. 007-elasticsearch5.4.3【一】概述、Elasticsearch 访问方式、Elasticsearch 面向文档、常用概念

    一.概述 Elasticsearch 是一个开源的搜索引擎,建立在一个全文搜索引擎库 Apache Lucene™ 基础之上. Elasticsearch 也是使用 Java 编写的,它的内部使用 L ...

  7. Elasticsearch 删除文档

    章节 Elasticsearch 基本概念 Elasticsearch 安装 Elasticsearch 使用集群 Elasticsearch 健康检查 Elasticsearch 列出索引 Elas ...

  8. Elasticsearch 更新文档

    章节 Elasticsearch 基本概念 Elasticsearch 安装 Elasticsearch 使用集群 Elasticsearch 健康检查 Elasticsearch 列出索引 Elas ...

  9. .Net Api 之如何使用Elasticsearch存储文档

    .Net Api 之如何使用Elasticsearch存储文档 什么是Elasticsearch? Elasticsearch 是一个分布式.高扩展.高实时的搜索与数据分析引擎.它能很方便的使大量数据 ...

随机推荐

  1. Salesforce 产品 | 协同办公“大魔王”,Salesforce Quip的使用攻略!

    Salesforce帮助企业渡过疫情难关,支持在线远程办公.7.5亿美金收购的动态文档共享平台Quip,即刻开放给所有Salesforce老客户还有非营利组织免费使用至2020年9月30日. Quip ...

  2. D - Harmonious Graph

    题目大意: n个点,m条边,两个数l和r,如果l和r相连接,那么对于l和r之间值任意一个数都要和l相连.问达到这一目的需要添加的边的最小数量. 题解: 我们首先要找到当前连通块中最大的那个点,也就是说 ...

  3. I. Same String

    有两个只由小写字母组成的长度为n的字符串s1,s2和m组字母对应关系,每一组关系由两个字母c1和c2组成,代表c1可以直接变成c2,你需要判断s1是否可以通过这m组关系转换为s2. 输入格式 第一行输 ...

  4. Git把本地代码推送到远程github仓库

    运用Git版本控制系统进行代码的管理,以便于团队成员的协作,由于之前是使用svn来进行版本控制,所以对于Git使用还有待熟练掌握.Git与svn类似,个人认为两者之间比较直观的区别就是 Git 不需要 ...

  5. [javascript] js实现小数的算术运算方法

    /** ** 加法函数,用来得到精确的加法结果 ** 说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显.这个函数返回较为精确的加法结果. ** 调用:accAdd(arg ...

  6. Service Mesh 介绍

    传统单体应用的局限性说明 传统单体应用代码体量庞大繁杂,不利于理解,也不利于团队合作开发,更不利于频繁更新和部署,增加服务宕机的风险. 耦合性高,功能代码块之前很容易造成强依赖,只要其中任何一个代码逻 ...

  7. TensorFlow keras vgg16net的使用

    from tensorflow.python.keras.applications.vgg16 import VGG16,preprocess_input,decode_predictions fro ...

  8. Git速查表大全

  9. <algorithm>中常用函数

    先说一下STL操作的区间是 [a, b),左边是闭区间,右边是开区间,这是STL的特性,所以<algorithm>里面的函数操作的区间也都是 [a, b). 先声明一下, sort()函数 ...

  10. 关于Swiper和vue数据顺序加载问题处理

    在使用swiper插件的时候,常常因为异步加载数据产生的顺序问题而使插件不能正常实行,所以可以使用vue的updated来解决. 问:什么时候 进updated方法? 答:只有事先设置好的data变量 ...