过滤顺序

bool 条件中过滤器的顺序对性能有很大的影响。更详细的过滤条件应该被放置在其他过滤器之前,以便在更早的排除更多的文档。

假如条件 A 匹配 1000 万个文档,而 B 只匹配 100 个文档,那么需要将 B 放在 A 前面。

缓存的过滤器非常快,所以它们需要被放在不能缓存的过滤器之前。想象一下我们有一个索引包含了一个月的日志事件,然而,我们只对近一个小时的事件感兴趣:

GET /logs/2014-01/_search
{
    "query" : {
        "filtered" : {
            "filter" : {
                "range" : {
                    "timestamp" : {
                        "gt" : "now-1h"
                    }
                }
            }
        }
    }
}

这个过滤条件没有被缓存,因为它使用了 now 方法,这个值每毫秒都在变化。这意味着我们需要每次执行这条查询时都检测一整个月的日志事件。

我们可以通过组合一个缓存的过滤器来让这变得更有效率:我们可以添加一个含固定时间的过滤器来排除掉这个月的大部分数据,例如昨晚凌晨:

"bool": {
    "must": [
        { "range" : {
            "timestamp" : {
                "gt" : "now-1h/d" <1>
            }
        }},
        { "range" : {
            "timestamp" : {
                "gt" : "now-1h" <2>
            }
        }}
    ]
}

<1> 这个过滤器被缓存了,因为它使用了取整到昨夜凌晨 now 条件。

<2> 这个过滤器没有被缓存,因为它没有对 now 取整。

now-1h/d 条件取整到昨夜凌晨,所以所有今天之前的文档都被排除掉了。这个结果的字节集被缓存了,因为 now 被取整了,意味着它只需要每天当昨夜凌晨的值改变时被执行一次。now-1h 条件没有被缓存,因为 now 表示最近一毫秒的时间。然而,得益于第一个过滤器,第二个过滤器只需要检测当天的文档就行。

这些条件的排序很重要。上面的实现能正常工作是因为自从昨晚凌晨条件比最近一小时条件位置更前。假如它们用别的方式组合,那么最近一小时条件还是需要检测所有的文档,而不仅仅是昨夜以来的文档。