执行分布式检索

一个 CRUD 操作只对单个文档进行处理,文档的唯一性由 _index, _type, 和 routing values (通常默认是该文档的 _id )的组合来确定。 这表示我们确切的知道集群中哪个分片含有此文档。

搜索需要一种更加复杂的执行模型因为我们不知道查询会命中哪些文档: 这些文档有可能在集群的任何分片上。 一个搜索请求必须询问我们关注的索引(index or indices)的所有分片的某个副本来确定它们是否含有任何匹配的文档。

但是找到所有的匹配文档仅仅完成事情的一半。 在 search 接口返回一个 page 结果之前,多分片中的结果必须组合成单个排序列表。 为此,搜索被执行成一个两阶段过程,我们称之为 query then fetch 。

1、查询阶段

在初始 查询阶段 时, 查询会广播到索引中每一个分片拷贝(主分片或者副本分片)。 每个分片在本地执行搜索并构建一个匹配文档的 优先队列

优先队列 
一个 优先队列 仅仅是一个存有 top-n 匹配文档的有序列表。优先队列的大小取决于分页参数 from 和 size 。例如,如下搜索请求将需要足够大的优先队列来放入100条文档。

GET /_search
{"from": 90,"size": 10
}

这个查询阶段的过程如下图所示。 

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

  • 1、客户端发送一个 search 请求到 Node 3 , Node 3 会创建一个大小为 from + size 的空优先队列。
  • 2、Node 3 将查询请求转发到索引的每个主分片或副本分片中。每个分片在本地执行查询并添加结果到大小为 from + size 的本地有序优先队列中。
  • 2、每个分片返回各自优先队列中所有文档的 ID 和排序值给协调节点,也就是 Node 3 ,它合并这些值到自己的优先队列中来产生一个全局排序后的结果列表。

当一个搜索请求被发送到某个节点时,这个节点就变成了协调节点。 这个节点的任务是广播查询请求到所有相关分片并将它们的响应整合成全局排序后的结果集合,这个结果集合会返回给客户端。

第一步是广播请求到索引中每一个节点的分片拷贝。就像 document GET requests 所描述的, 查询请求可以被某个主分片或某个副本分片处理, 这就是为什么更多的副本(当结合更多的硬件)能够增加搜索吞吐率。 协调节点将在之后的请求中轮询所有的分片拷贝来分摊负载。

每个分片在本地执行查询请求并且创建一个长度为 from + size 的优先队列—也就是说,每个分片创建的结果集足够大,均可以满足全局的搜索请求。 分片返回一个轻量级的结果列表到协调节点,它仅包含文档 ID 集合以及任何排序需要用到的值,例如 _score 。

协调节点将这些分片级的结果合并到自己的有序优先队列里,它代表了全局排序结果集合。至此查询过程结束。

注意: 
一个索引可以由一个或几个主分片组成, 所以一个针对单个索引的搜索请求需要能够把来自多个分片的结果组合起来。 针对 multiple 或者 all 索引的搜索工作方式也是完全一致的–仅仅是包含了更多的分片而已。

2、取回阶段

查询阶段标识哪些文档满足 搜索请求,但是我们仍然需要取回这些文档。这是取回阶段的任务, 正如下图所示。

 
分布式阶段由以下步骤构成:

  • 1、协调节点辨别出哪些文档需要被取回并向相关的分片提交多个 GET 请求。
  • 2、每个分片加载并 丰富 文档,如果有需要的话,接着返回文档给协调节点。
  • 3、一旦所有的文档都被取回了,协调节点返回结果给客户端。

协调节点首先决定哪些文档 确实 需要被取回。例如,如果我们的查询指定了 { “from”: 90, “size”: 10 } ,最初的90个结果会被丢弃,只有从第91个开始的10个结果需要被取回。这些文档可能来自和最初搜索请求有关的一个、多个甚至全部分片。

协调节点给持有相关文档的每个分片创建一个 multi-get request ,并发送请求给同样处理查询阶段的分片副本。

分片加载文档体– _source 字段–如果有需要,用元数据和 search snippet highlighting 丰富结果文档。 一旦协调节点接收到所有的结果文档,它就组装这些结果为单个响应返回给客户端。

深分页(Deep Pagination)

先查后取的过程支持用 from 和 size 参数分页,但是这是 有限制的 。 要记住需要传递信息给协调节点的每个分片必须先创建一个 from + size 长度的队列,协调节点需要根据 number_of_shards * (from + size) 排序文档,来找到被包含在 size 里的文档。

取决于你的文档的大小,分片的数量和你使用的硬件,给 10,000 到 50,000 的结果文档深分页( 1,000 到 5,000 页)是完全可行的。但是使用足够大的 from 值,排序过程可能会变得非常沉重,使用大量的CPU、内存和带宽。因为这个原因,我们强烈建议你不要使用深分页。

实际上, “深分页” 很少符合人的行为。当2到3页过去以后,人会停止翻页,并且改变搜索标准。会不知疲倦地一页一页的获取网页直到你的服务崩溃的罪魁祸首一般是机器人或者web spider。

如果你 确实 需要从你的集群取回大量的文档,你可以通过用 scroll 查询禁用排序使这个取回行为更有效率。

3、搜索选项

有几个 查询参数可以影响搜索过程。

3.1、偏好

偏好这个参数 preference 允许 用来控制由哪些分片或节点来处理搜索请求。 它接受像 _primary, _primary_first, _local, _only_node:xyz, _prefer_node:xyz, 和 _shards:2,3 这样的值, 这些值在 search preference 文档页面被详细解释。

但是最有用的值是某些随机字符串,它可以避免 bouncing results 问题。

Bouncing Results 
想象一下有两个文档有同样值的时间戳字段,搜索结果用 timestamp 字段来排序。 由于搜索请求是在所有有效的分片副本间轮询的,那就有可能发生主分片处理请求时,这两个文档是一种顺序, 而副本分片处理请求时又是另一种顺序。

这就是所谓的 bouncing results 问题: 每次用户刷新页面,搜索结果表现是不同的顺序。 让同一个用户始终使用同一个分片,这样可以避免这种问题, 可以设置 preference 参数为一个特定的任意值比如用户会话ID来解决。

3.2、超时问题

通常分片处理完它所有的数据后再把结果返回给协同节点,协同节点把收到的所有结果合并为最终结果。

这意味着花费的时间是最慢分片的处理时间加结果合并的时间。如果有一个节点有问题,就会导致所有的响应缓慢。

参数 timeout 告诉 分片允许处理数据的最大时间。如果没有足够的时间处理所有数据,这个分片的结果可以是部分的,甚至是空数据。

搜索的返回结果会用属性 timed_out 标明分片是否返回的是部分结果:

"timed_out":     true, 

注意: 
超时仍然是一个最有效的操作,知道这一点很重要; 很可能查询会超过设定的超时时间。这种行为有两个原因:

  • 1、超时检查是基于每文档做的。 但是某些查询类型有大量的工作在文档评估之前需要完成。 这种 “setup” 阶段并不考虑超时设置,所以太长的建立时间会导致超过超时时间的整体延迟。
  • 2、因为时间检查是基于每个文档的,一次长时间查询在单个文档上执行并且在下个文档被评估之前不会超时。 这也意味着差的脚本(比如带无限循环的脚本)将会永远执行下去。

3.3、路由

在 路由一个文档到一个分片中 中, 我们解释过如何定制参数 routing ,它能够在索引时提供来确保相关的文档,比如属于某个用户的文档被存储在某个分片上。 在搜索的时候,不用搜索索引的所有分片,而是通过指定几个 routing 值来限定只搜索几个相关的分片:

GET /_search?routing=user_1,user2

3.4、搜索类型

缺省的搜索类型是 query_then_fetch 。 在某些情况下,你可能想明确设置 search_type 为 dfs_query_then_fetch 来改善相关性精确度:

GET /_search?search_type=dfs_query_then_fetch

搜索类型 dfs_query_then_fetch 有预查询阶段,这个阶段可以从所有相关分片获取词频来计算全局词频。

4、游标查询 Scroll

scroll 查询 可以用来对 Elasticsearch 有效地执行大批量的文档查询,而又不用付出深度分页那种代价。

游标查询允许我们 先做查询初始化,然后再批量地拉取结果。 这有点儿像传统数据库中的 cursor 。

游标查询会取某个时间点的快照数据。 查询初始化之后索引上的任何变化会被它忽略。 它通过保存旧的数据文件来实现这个特性,结果就像保留初始化时的索引 视图 一样。

深度分页的代价根源是结果集全局排序,如果去掉全局排序的特性的话查询结果的成本就会很低。 游标查询用字段 _doc 来排序。 这个指令让 Elasticsearch 仅仅从还有结果的分片返回下一批结果。

启用游标查询可以通过在查询的时候设置参数 scroll 的值为我们期望的游标查询的过期时间。 游标查询的过期时间会在每次做查询的时候刷新,所以这个时间只需要足够处理当前批的结果就可以了,而不是处理查询结果的所有文档的所需时间。 这个过期时间的参数很重要,因为保持这个游标查询窗口需要消耗资源,所以我们期望如果不再需要维护这种资源就该早点儿释放掉。 设置这个超时能够让 Elasticsearch 在稍后空闲的时候自动释放这部分资源。

GET /old_index/_search?scroll=1m
{"query": { "match_all": {}},"sort" : ["_doc"], "size":  1000
}

保持游标查询窗口一分钟。

关键字 _doc 是最有效的排序顺序。

这个查询的返回结果包括一个字段 _scroll_id, 它是一个base64编码的长字符串 ((("scroll_id"))) 。 现在我们能传递字段_scroll_id 到 _search/scroll 查询接口获取下一批结果:

GET /_search/scroll
{"scroll": "1m", "scroll_id" : "cXVlcnlUaGVuRmV0Y2g7NTsxMDk5NDpkUmpiR2FjOFNhNnlCM1ZDMWpWYnRROzEwOTk1OmRSamJHYWM4U2E2eUIzVkMxalZidFE7MTA5OTM6ZFJqYkdhYzhTYTZ5QjNWQzFqVmJ0UTsxMTE5MDpBVUtwN2lxc1FLZV8yRGVjWlI2QUVBOzEwOTk2OmRSamJHYWM4U2E2eUIzVkMxalZidFE7MDs="
}

注意再次设置游标查询过期时间为一分钟。

这个游标查询返回的下一批结果。 尽管我们指定字段 size 的值为1000,我们有可能取到超过这个值数量的文档。 当查询的时候, 字段 size 作用于单个分片,所以每个批次实际返回的文档数量最大为 size * number_of_primary_shards 。

注意: 
注意游标查询每次返回一个新字段 _scroll_id。每次我们做下一次游标查询, 我们必须把前一次查询返回的字段_scroll_id 传递进去。 当没有更多的结果返回的时候,我们就处理完所有匹配的文档了。

深入理解ElasticSearch(七):执行分布式检索相关推荐

  1. 《深入理解Elasticsearch(原书第2版)》——第2章 查询DSL进阶 2.1 Apache Lucene默认评分公式解释...

    本节书摘来自华章计算机<深入理解Elasticsearch(原书第2版)>一书中的第2章,第2.1节,作者 [美]拉斐尔·酷奇(Rafal Ku)马雷克·罗戈任斯基(Marek Rogoz ...

  2. 《深入理解Elasticsearch》读书笔记

    题记 由于之前已经梳理过Elasticsearch基础概念且在项目中实战过Elasticsearch的增删改查.聚类.排序等相关操作,对ES算是有了一定的认知. 但是,仍然对于一些底层的原理认知模糊, ...

  3. 《深入理解Elasticsearch(原书第2版)》——第1章  Elasticsearch简介

    2019独角兽企业重金招聘Python工程师标准>>> 第1章  Elasticsearch简介 摘要: 欢迎来到Elasticsearch的世界并阅读本书第2版.通过阅读本书,我们 ...

  4. 干货 |《深入理解Elasticsearch》读书笔记

    题记 由于之前已经梳理过Elasticsearch基础概念且在项目中实战过Elasticsearch的增删改查.聚类.排序等相关操作,对ES算是有了一定的认知. 但是,仍然对于一些底层的原理认知模糊, ...

  5. ElasticSearch、上架与检索

    ElasticSearch.上架与检索 目录 ElasticSearch.上架与检索 一.ELASTIC SEARCH 0.简介 1.安装elastic search 2.初步检索 二.进阶检索 三. ...

  6. ElasticSearch(七) 搜索

    title: ElasticSearch(七) 搜索 tags: ElasticSearch author: Clown95 搜索 在前面,已经介绍了在ElasticSearch索引中处理数据的基础知 ...

  7. ElasticSearch基本原理和分布式文件系统

    目录 阶段一:Elasticsearch概念与架构 Elasticsearch的功能 Elasticsearch-Linux安装 Elasticsearch核心概念 Elasticsearch基础分布 ...

  8. 《深入理解ElasticSearch》——2.4 批量操作

    本节书摘来自华章计算机<深入理解ElasticSearch>一书中的第2章,第2.4节,作者:[美] 拉斐尔·酷奇(Rafa Ku) 马雷克·罗戈任斯基(Marek Rogoziński) ...

  9. 《深入理解Elasticsearch(原书第2版)》一2.2 查询改写

    本节书摘来自华章出版社<深入理解Elasticsearch(原书第2版)>一书中的第2章,第2.2节,作者[美]拉斐尔·酷奇(Rafal Ku) 马雷克·罗戈任斯基(Marek Rogoz ...

最新文章

  1. php留言板入门教程,一个php留言板实例详解(附源码下载)
  2. 微信小游戏开发教程-游戏实现1
  3. form 中Enctype=multipart/form-data 的作用
  4. Java Post 数据请求和接收
  5. 子窗体与父窗体之间相互调用其方法的实现
  6. 【差分】bzoj 1676 [Usaco2005 Feb]Feed Accounting 饲料计算
  7. HTTP 301 跳转和302跳转的区别
  8. linux sql 语句菜鸟,Linux安装mysql
  9. 9月26日云栖精选夜读:阿里Java代码规约插件即将全球首发,邀您来发布仪式现场...
  10. 递归与递推类型题小结
  11. NETARM(NSMS)自定义万能表单系统使用说明
  12. 金士顿优盘不被电脑识别的小技巧
  13. java 控制层和业务层,控制层、业务层和数据访问层
  14. 与引导文件系统/vmfs/devices..的备用设备之间的连接已丢失,主机配置更改将不会保存到持久存储中...
  15. 使用CCleaner删除系统还原点
  16. Ubuntu 20.04 -中文输入法-fcitx-connection failed [ip:91.189.91.38 80]
  17. ThreadLocal使用时因线程复用导致数据混乱分析
  18. @Autowired @Resources @Injected 三者的区别联系
  19. 什么是递归查询,迭代查询?
  20. Lind.DDD.Domain领域模型介绍

热门文章

  1. shell脚本每日一练(一)
  2. 分级显示HTML,SSM框架下,以tree结构分级显示数据
  3. linux内核替换图片,linux内核替换 - magic_吕伟的个人空间 - OSCHINA - 中文开源技术交流社区...
  4. 查看某个方法在哪里被调用_一篇文章带你查看并处理Pandas数据
  5. Mysql价格降低20%应该怎么写_mysql优化20条原则
  6. 实现option上下移动_Perona-Malik方程(各向同性非线性扩散实现图像滤波)
  7. 51单片机计算机实物焊接,基于51单片机的最小系统焊接图 浅谈单片机最小系统...
  8. otg usb 定位_详解USB OTG工作原理及其应用
  9. 中南大学计算机在线考试答案,中南大学计算机考试复习题
  10. android studio真机模拟不能拍照_android和ios静态库的生成