ElasticSearch学习笔记(九)——Elasticsearch查询原理分析

一、前言

ES的查询分为两个阶段:

  • 查询
  • 取回

首先,一个 index 的数据会被分为多片,所以一个 document ,只能存在于一个 shard 中,如何确定doc的位置呢?

数据路由算法:

  • shard = hash(routing) % number_of_primary_shards

  • routing 可以是_id(默认)或者由我们自定义传入。

手动指定
在发送请求的时候,手动指定一个 routing value,比如使用

put /index/type/id?routing=user_id

二、查询阶段

查询阶段流程分析:

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

相关知识点:

  • 当一个搜索请求被发送到某个节点时,这个节点就变成了协调节点
  • 查询请求可以被某个主分片或某个副本分片处理,此时会使用 round-robin 随机轮询算法,在 primary shard 以及其所有 replica 中随机选择一个,让读请求负载均衡
  • 每个分片在本地执行查询请求并且创建一个长度为 from + size 的优先队列—也就是说,每个分片创建的结果集足够大,均可以满足全局的搜索请求。
  • 分片返回一个轻量级的结果列表到协调节点,它仅包含文档 ID 集合以及任何排序需要用到的值,例如 _score

三、取回阶段

查询阶段标识哪些文档满足搜索请求,但是我们仍然需要取回这些文档。

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

四、深分页

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

举例:要查询1000页数据,from = 1000,size = 10,就是查询10001—10010数据,假设集群有3个primary shard,那么coordinate会将请求分别发送到这三个分片,每个分片都会创建一个from + size 长度的优先级队列,这里就是查询10010条数据,然后将数据返回给coordinate,coordinate会拿到30030条数据,按照from,size定义,取10001—10010数据。

所以在分页过深时,首先就是查询的文档数据量太多,其次还要对文档进行排序,这会大量占用CPU、内存和带宽,所以一般对es的分页会做限制

五、游标查询scroll

如果一次性要查出来比如 10万条 数据,那么性能会很差,此时一般会采取用 scoll 滚动查询,一批一批的查,直到所有数据都查询完处理完

scoll 搜索会在第一次搜索的时候,保存一个当时的视图快照,之后只会基于该旧的视图快照提供数据搜索,如果这个期间数据变更,是不会让用户看到的

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

每次发送 scroll 请求,我们还需要指定一个 scoll 参数,指定一个时间窗口,每次搜索请求只要在这个时间窗口内能完成就可以,而不是处理查询结果的所有文档的所需时间。保持这个游标查询窗口需要消耗资源,所以我们期望如果不再需要维护这种资源就该早点儿释放掉

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

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

获得的结果会有一个 scoll_id,下一次再发送 scoll 请求的时候,必须带上这个 scoll_id
字段 _scroll_id,它是一个base64编码的长字符串

小结:
  • scroll 时间窗口不用每次都携带,貌似是每次都延长时间
  • scoll 看起来挺像分页的,但是其实使用场景不一样。
  • 分页主要是用来一页一页搜索,给用户看的
  • scoll 主要是用来一批一批检索数据,让系统进行处理的
参考

Elasticsearch权威指南

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值