ElasticSearch基础之分布式查询的执行

我们在之前的《ElasticSearch基础之分布式存储》中详细介绍了ElasticSearch是如何进行分布式的读写(CRUD)。那我们在发送查找请求之后,ElasticSearch是如何进行分布式执行的呢?它其实要比我们前面提到的CRUD操作复杂一些,本文就来详细介绍一下这一方面的知识。

前面的CRUD操作其实只是针对某一个shard的,也就是说我们其实是知道我们的操作最终会指向哪一个shard的, 而search相对来说复杂的地方就在于我们根本不知道要查找的内容是存储在哪一个shard上,这就必然导致我们需要去访问每一个shard,或者说至少是我们感兴趣的shard。然后访问不同(所有)shard得到的数据其实只完成了一部分的工作,你还需要把各个shard返回的内容进行整合,排序或者筛选出最终返回给用户的那一部分数据并把它们从各个shard提取出来。因此我们可以简单认为search其实包含了两个部分的操作,一个是查询一个是提取。

查询

查询过程其实说白了也很简单,它在开始的时候会把你的query发送到index上所有的shard(有可能是primary也可能是replica的shard),然后在各个shard上执行查询,并根据相匹配的文档生成一个priority queue。具体可以参见下面的流程图:

  1. 首先假设client端会把search的request发送到node3,这里首先会create一个空的priority queue,大小就是from+size。这里from就是页码,比如说我们每页返回10条记录,我想看第三页的,那么from这里就设为20,size就是10。
  2. Node3会把这个request转发给cluster中所有的相关shard,可能是primary也可能是replica,每个shard会在本地执行相关的request,并把它们的结果加入到本地的priority queue中,每个priority queue的大小也是from+size。
  3. 每个shard都会把它们的结果返回给node3,node3这里会进行merge的操作,同样取最前面的from+size放到全局的priority queue中。

整个流程还是蛮清晰的,node3负责接收request和返回response,它也负责分发request到各个shard,并merge最终得到的结果。这里假如你仔细观察会发现其实from这个值对我们需要查询的内容有很大的影响,同样是查询10条数据,假如from==0,也就是第一页,你就只要查询10条,但是假如from是9,也就是第10页,它就需要查询100条数据,这在分散到各个shard上,查询的数据就更大了。这也是为什么有些查询不支持太多的页,一方面是因为排序在后面的内容其实意义也不是很大,另一方面也是考虑这个查询的效率问题。

提取

上面查询就是确定哪些文本是符合最终的需求的,在有了这些信息之后,下一步要做的就是把这些文本提取出来,这就是提取这一步要做的事情,大体的流程见下图:

  1. Node3从查询阶段得到了符合要求的文本列表,它会根据这个列表发送Get请求到各个shard来获取相关的文本内容。
  2. 每一个shard都加载相关的文本,并有可能附加一些额外的信息(metadata或者高亮文本之类的),然后返回给node3.
  3. Node3接收到所有的文本内容,返回给client端。

Node3在第一步会计算一下哪些文本真正的需要提取,比如说像我们上面提到的例子,from是9,那么其实前面的90条记录是不需要去进行提取的,只需要提取91到100这一范围的文本就可以了。

查询的选项

上面提到的整个流程其实是一个通用情况的流程,其实有很多查询参数也会影响这个流程:

Preference

假如你仔细阅读上面的流程,你会发现我们在查询的时候并不一定是要去访问所有的shard,这里其实是可以控制的,甚至哪些node需要访问都是可以通过preference来设置的。

你可以设置这个参数为_primary, _primary_first, _local, _only_node:xyz,_prefer_node:xyz或者_shards:2,3等等,不需要我详细介绍各个设置的意义应该还是蛮明显的。

Timeout

这个也比较好理解,我们毕竟要访问很多不同的shard,这个过程中什么都有可能发生,不可能无限制的等待下去,所以会有一个超时的机制,当超过了这段时间我们就不再等待相应shard的返回了,这个结果会显示在最终的返回结果中,如下所示:

这里就是有超时发生,总共有5个shard,其中有一个没有在规定时间内返回结果。我们也可以通过这个域来判断是不是所有的shard都出现了问题。

Routing

我们在index的时候可以根据routing来把相关的文档都放在同样的shard中,那么显然在查询的时候我们也可以通过设置这个参数来控制查询所涉及的shard。比如说下面这个request就只touch user1和user2所在的shard。

Search_type

其实我们上面提到的查询-提取只是一个默认的操作,我们是可以通过search_type来控制的,它可以设置为下面这些值:

  • Count:这个就只有查询的步骤,不会去进行提取。一般用在我们不需要知道具体文档的情况下,比如有多少个查询结果之类的。
  • Query_and_fetch:这个就是我们上面提到的查询和提取。
  • Dfs_query_then_fetch/dfs_quey_and_fetchg:这个设置中有一个预查询的步骤,他们会从所有相关的shard中获取term的frequency来决定一个global的term frequency。这个主要是用来防止relevance计算出问题。我们在《Elasticsearch基础之相关性介绍》中详细介绍了relevance的计算,这里有一个概念,就是相关性其实和term出现的频率有关系,假如term在某一个shard中出现的频率太高或者太低,那么就会影响它在那个shard中分数的计算,从而在我们合并多个shard进行比较的时候不公平,所以这里我们会通过这个设置来得到一个全局的频率从而减小这个误差。当然在数据量很大的系统中,没有必要这么做,这个在一些比较小数据量的测试系统中会常常遇到。
  • Scan:这个主要是和scroll来配合的,主要是用来高效查询大数据量的结果,它不会进行各个shard之间的排序。主要是用来快速返回大量数据的情况下。

总结

至此本文就详细介绍了ElasticSearch是如何进行分布式查询的,并介绍了几个常见的参数。

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *