Elasticsearch 基础:数据搜索流程

ES 数据搜索流程

ES 的数据召回是一个比较复杂的流程,因为绝大多数的 ES 都是以集群形式存在的,同时由于存储结构等方面的因素,数据在进行召回打分时需要经历很长的链路才能最终返回。

主线流程

  1. 接受请求
  2. 路由与寻址
  3. 分片选择和请求转发
  4. 执行搜索计划
  5. 搜索的后处理
  6. 返回结果

接受请求

ES 集群中的所有节点理论上都可以接受搜索请求,其中无论是通过普通的 _search 接口还是 url 等方式只是程序的入口不同,中间的 query 解析的部分会有些许差别,后台对 query 的解析和处理逻辑是一致的。事实上,接受请求的那个节点会被称为coordinating node 协调节点,因为它会对 query 进行分析并“协调”资源以完成整个搜索过程。所以对于较大的需要承担较大负载的集群,一般会建议设置独立的拥有较大量资源(内存 + CPU)的节点作为专属的协调节点,以应对大请求,同时减小对数据节点和其他节点(如 ingest)节点的资源竞争。

路由与寻址

coordinating node 接收了请求之后,它就需要将该请求转发到合适的索引、节点和分片上,有时,一个请求可能会横跨多个索引,所有索引、分片的寻址也会在这里进行处理。大致顺序如下:

  1. coordinating node 会根据 query 的设置构建一个最终的目标索引列表,因为有时 query 里会出现类似 index1,index2 的多索引指定,以及 index* 这种通过通配符匹配多个索引的情况。
  2. coordinating node 会根据这个索引列表构建分片列表,由于副本的存在,一个索引中同样 ID 的分片(主分片 + 副本)可能会存在多个(它们会通过 pr 来区分主分片和副本),所以这里会对分片 ID 进行去重,得到一个去重之后的分片 ID 列表。
  3. 根据路由选项(如 _routing),coordinating node 会确认当前的 query 会发给某一指定分片还是所有分片进行后续的召回。

分片选择和请求转发

无论请求会被单个分片还是所有分片处理,coordinating node 都会在这一步里确定选择目标 ID 对应的主分片还是副本(如果存在),因为最终有且只有一个分片(或者副本)会用来处理这个 query。默认的分片选择算法是带部分优化的随机选择,当然,这个选择算法也会被 preference 的选项所覆盖(如设为_local_等)。在找到目标分片之后,coordinating node 会将 query 转发给目标分片所在的节点,再由这些节点(也可能是自己)把 query 发给目标分片进行后续操作。

执行搜索计划

在这一步中,query 正式进入所有目标分片,每个分片都是个 lucene 的实例,所以这里会对 query 进行进一步的解析,包括找到目标字段,将 ESquery 转化为 lucenequery 命令,对 query 内容使用索引时的分词器进行拆分,在每个 lucene 实例中进行召回和打分。

每个 lucene 的搜索都是一系列的已经被提交了的 segment (段)的召回、排序、merge 的过程,所以大部分的缓存也会发生在这个阶段。对于 segment 个数来说,更少的段会让 ES 在搜索过程中扫描更少的段,相对来说速度会更快,所以在优化中会有一个手动进行段合并的操作。这个阶段中 lucene 会通过对 segment 的搜索尝试构建一个目标文档 ID 的列表,然后再根据指定的字段进行数据的读取,并进行后续的排序和聚合操作。

聚合操作有时会需要把所有目标文档纳入处理,所有分片(索引)都会把整个结果集返回 coordinating node,由它进行统一的 merge(所以 coordinating node 需要足够的资源进行计算)。在某些聚合中可能会存在数据不精确,是因为 ES 会对列表进行一部分截取,比如 terms 聚合时,默认会处理 size * 1.5 + 10 条数据 —— 指定 100 条数据时每个分片会返回 160 条数据进入最终处理。

缓存(目前)是 LRU 的,主要会包含当前 filter 条件中的文档列表,最近被召回的文档字段等信息。每个索引还会维护一个自己的 query cache,用来直接响应短时间里的相同 query,不过这个缓存在索引做 refresh 之后会被清掉。

每个 ES 的数据节点都会维护 translog 这个类似 K-V 结构的存储,所以如果使用的是 getById 的接口,ES 就不会构建请求而是直接从 translog 进行文档的召回了。

搜索的后处理

在搜索流程中,所有分片的召回结果都会汇总在协调节点,由它来做最后的 merge 操作,数据处理条数会按 query 中设置的不同而不同(默认是 10);如设置为 10,则每个分片会在排序之后返回 10 条数据给协调节点,协调节点再对这里的 10 * 分片个数 条数据进行排序,返回前 10 条给请求方。对于横跨多个索引的 query 也一样,也会做一次整个结果集的 reduce

这里可以看出,整个搜索流程中 coordinating node 需要处理包括数据列表的排序和 merge,聚合统计,最终返回结果的组织等,所以要保证 coordinating node 有足够的资源,否则很可能在处理过程中出现 OOM 等故障,同时(在高负载、大数据量的集群中)也需要尽量将 coordinating node 独立出来,以免其他的处理任务和正常的搜索任务出现资源的竞争。比如在深度分页的场景中,每次 coordinating node 需要处理的数据为 分片数 * (from + size),而且这个数据量会随着页数(from)的增长而迅速上升,这也是在深度分页场景建议使用 scroll 或者 search_after 的原因。

返回结果

在得到最终的文档列表之后,ES 会根据 query 里的设置,从每个分片里获取指定字段,并返回调用方(通过 _source 属性指定)。这个过程就用到了那个类似 getById 的功能(_mget),所以如果不需要原始的文档信息(比如聚合操作),可以通过将 size 设为 0 来跳过这一步。

总结

ES 是一个复杂的分布式系统,所以数据的搜索流程和索引流程都会是一个很长很复杂的链路,用以优化整个流程和应对各类分布式问题等。在搜索流程中会存在各种不可控的因素,所以 ES 会在出现异常(timeoutshard failure等)时尝试通过 retry 等方式进行补救,以尽最大可能相应请求,在遇到自己 retry 也无法完成的状况时,ES 也会尝试通过返回部分结果的方式进行响应,尽可能避免无结果的返回。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值