Elasticsearch的search之_shards skipped之谜

es从 v5.6 开始引入了 pre-filter 机制(skipped):对于 Date 类型的 Range 查询,在对分片执行搜索之前,先检查一下分片是否包括被查询的数据范围,如果查询的范围与分片持有的数据没有交集,就跳过该分片。

"_shards":{
    "total": 130,
    "successful": 130,
    "skipped": 2,
    "failed": 0
}

skipped

(Integer) Number of shards that skipped the request because a lightweight check helped realize that no documents could possibly match on this shard. This typically happens when a search request includes a range filter and the shard only has values that fall outside of that range.

基本原理

搜索过程原先由两个阶段执行(查询阶段和取回阶段)变成三个阶段(新增:预过滤阶段【pre-filter】),pre-filter 在查询阶段之前执行。

怎样预过滤?

协调节点收到客户端的查询请求后,向本次搜索涉及到的全部分片发送RPC请求:indices:data/read/search[can_match],每个节点收到请求后,判断请求的范围和待查询的分片是否存在交集,返回是或否,然后协调节点跳过不存在交集的分片,向其他分片发送下一阶段(查询阶段)的请求。

这次 RPC 请求以 shard 为单位并行发送,没有并发限制。待查询的 shard 有多少个,就并发发送多少个 RPC。然后等待全部 RPC 返回响应。

此时发送的 RPC 请求没有超时限制。事实上,_search 请求的 timeout参数仅在整个分布式搜索的 query 阶段进行检查,并且不包括 PRC 层面,他只在数据节点收到协调节点发来的 RPC 后开始计时,检查 query 过程是否超时。fetch 阶段的 RPC,以及数据节点对 fetch 请求的处理均没有超时检查。

什么情况下会执行?

pre-filter 并不会在所有查询过程中执行,在 v7.4中,需要同时满足以下条件,才会执行 pre-filter :

  • 待查询的分片数大于 128(在url中指定pre_filter_shard_size)
  • 聚合请求不要求访问所有 doc。即非 Global Aggregation 或 "min_doc_count" 不为0

另外,非 Date 类型的数值查询虽然也会走 pre-filter流程,但内部不会去判断范围,虽然协调节点也会发送 can_match 的 RPC,但数据节点的响应会在 MappedFieldType#isFieldWithinQuery 中直接返回相交,所以没有分片会被 skip,未来这方面可能会有扩展。

关于pre-filter 的实现原理

它依赖于 Lucene 的一个重要特性:PointValues ,在早期的版本中数值类型当字符串存入倒排索引但范围查询效率比较低,从 Lucene 6.0开始数值类型使用 BKD-Tree 来建立索引(PointValues,它在多维、一维数值查询很棒,IntField->IntPoint,LongField->LongPoint......)

关于BKD-Tree笔者理解也不深刻,看源码也是稍微领略其中要到,Lucene为每个数值字段建立索引,这个索引就是一个二叉树,我们知道一个Index由多个Segment组成,每个Segment中每个数值字段的索引即为一个KDB-Tree,在Segment Merge的过程中,多个KDB-Tree会进行合并,生成一个较大的KDB-Tree。最重要的是:可以轻松获取到每个 segment 中该字段的最大值和最小值,reader读取某个segment 时, 内部的 BKDReader 会将最大值和最小值读取出来常驻 JVM 内存。

因此, pre-filter 只在 Date 类型的Range 查询里实现了,因为 Date 类型的数值确定是递增的,其他数值类型未必。对于非递增的数值字段,其数据会散布到 my_index-* 的每个分片上,因此 pre-filter 也就没有必要了。

Elasticsearch内部实现非常复杂,因此我们在学习它的时候,要大胆推测,细心验证,才能领会其中要领。

参考:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值