查询阶段
- 在初始化查询阶段(query phase),查询被向索引中的每个分片副本(原本或副本)广播。
- 每个分片在本地执行搜索并且建立了匹配document的优先队列(priority queue)。
优先队列
一个优先队列(priority queue is)只是一个存有前n个(top-n)匹配document的有序列表。这个优先队列的大小由分页参数from和size决定。例如,下面这个例子中的搜索请求要求优先队列要能够容纳100个document
```
GET /_search
{
"from": 90,
"size": 10
}
```
- 这个查询的过程被描述在图分布式搜索查询阶段中。
4.查询阶段包含以下三步
1. 客户端发送一个search(搜索)请求给Node 3,Node 3创建了一个长度为from+size的空优先级队列。
2. Node 3 转发这个搜索请求到索引中每个分片的原本或副本。每个分片在本地执行这个查询并且结果将结果到一个大小为from+size的有序本地优先队列里去。
3. 每个分片返回document的ID和它优先队列里的所有document的排序值给协调节点Node 3。Node 3把这些值合并到自己的优先队列里产生全局排序结果。
- 当一个搜索请求被发送到一个节点Node,这个节点就变成了协调节点。这个节点的工作是向所有相关的分片广播搜索请求并且把它们的响应整合成一个全局的有序结果集。这个结果集会被返回给客户端。
- 整个过程类似于归并排序算法,先分组排序再归并到一起,对于这种分布式场景非常适用。
取回阶段
查询阶段辨别出那些满足搜索请求的document,但我们仍然需要取回那些document本身。这就是取回阶段的工作,如图分布式搜索的取回阶段所示。
-
分发阶段由以下步骤构成
- 协调节点辨别出哪个document需要取回,并且向相关分片发出GET请求。
- 每个分片加载document并且根据需要丰富(enrich)它们,然后再将document返回协调节点。
- 一旦所有的document都被取回,协调节点会将结果返回给客户端。
协调节点先决定哪些document是实际(actually)需要取回的。例如,我们指定查询{ "from": 90, "size": 10 },那么前90条将会被丢弃,只有之后的10条会需要取回。这些document可能来自与原始查询请求相关的某个、某些或者全部分片。
搜索选项
一些查询字符串(query-string)可选参数能够影响搜索过程
-
preference(偏爱)
- preference参数允许你控制使用哪个分片或节点来处理搜索请求。她接受如下一些参数 _primary, _primary_first, _local, _only_node:xyz, _prefer_node:xyz和_shards:2,3
- 具体含义参考 https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-preference.html
-
结果震荡(Bouncing Results)
- 想像一下,你正在按照timestamp字段来对你的结果排序,并且有两个document有相同的timestamp。由于搜索请求是在所有有效的分片副本间轮询的,这两个document可能在原始分片里是一种顺序,在副本分片里是另一种顺序。
- 这就是被称为结果震荡(bouncing results)的问题:用户每次刷新页面,结果顺序会发生变化。避免这个问题方法是对于同一个用户总是使用同一个分片。方法就是使用一个随机字符串例如用户的会话ID(session ID)来设置preference参数。
-
timeout(超时)
- timeout参数告诉协调节点最多等待多久,就可以放弃等待而将已有结果返回。返回部分结果总比什么都没有好。
- 搜索请求的返回将会指出这个搜索是否超时,以及有多少分片成功答复了:
...
"timed_out": true, //搜索请求超时
"_shards": {
"total": 5,
"successful": 4,
"failed": 1 //五个分片中有一个没在超时时间内答复
},
... -
routing(路由选择)
- 在路由值那节里,我们解释了如何在建立索引时提供一个自定义的routing参数来保证所有相关的document(如属于单个用户的document)被存放在一个单独的分片中。
- 在搜索时,你可以指定一个或多个routing 值来限制只搜索那些分片而不是搜索index里的全部分片:
GET /_search?routing=user_1,user2
- 这个技术在设计非常大的搜索系统时就会派上用场了。
-
search_type(搜索类型)
- 虽然query_then_fetch是默认的搜索类型,但也可以根据特定目的指定其它的搜索类型,例如:
GET /_search?search_type=count
- count(计数)
- count(计数)搜索类型只有一个query(查询)的阶段。当不需要搜索结果只需要知道满足查询的document的数量时,可以使用这个查询类型。
- query_and_fetch(查询并且取回)
- query_and_fetch(查询并且取回)搜索类型将查询和取回阶段合并成一个步骤。这是一个内部优化选项,当搜索请求的目标只是一个分片时可以使用,例如指定了routing(路由选择)值时。虽然你可以手动选择使用这个搜索类型,但是这么做基本上不会有什么效果。
- dfs_query_then_fetch 和 dfs_query_and_fetch
- dfs搜索类型有一个预查询的阶段,它会从全部相关的分片里取回项目频数来计算全局的项目频数。
- scan(扫描)
- scan(扫描)搜索类型是和scroll(滚屏)API连在一起使用的,可以高效地取回巨大数量的结果。它是通过禁用排序来实现的。
扫描和滚屏
- scroll(滚屏)
- 一个滚屏搜索允许我们做一个初始阶段搜索并且持续批量从Elasticsearch里拉取结果直到没有结果剩下。
- 滚屏搜索会及时制作快照。这个快照不会包含任何在初始阶段搜索请求后对index做的修改。它通过将旧的数据文件保存在手边,所以可以保护index的样子看起来像搜索开始时的样子。
- scan(扫描)
- 深度分页代价最高的部分是对结果的全局排序,但如果禁用排序,就能以很低的代价获得全部返回结果。
- 为达成这个目的,可以采用scan(扫描)搜索模式。扫描模式让Elasticsearch不排序,只要分片里还有结果可以返回,就返回一批结果。
- 为了使用scan-and-scroll(扫描和滚屏),需要执行一个搜索请求,将search_type 设置成scan,并且传递一个scroll参数来告诉Elasticsearch滚屏应该持续多长时间。
GET /old_index/_search?search_type=scan&scroll=1m (1)
{
"query": { "match_all": {}},
"size": 1000
} - es5.0之后上面的方式就用不到了,参考