关于缓存

在【内部过滤操作】章节中,我们简单提到过过滤器是怎么计算的。它们的核心是一个字节集来表示哪些文档符合这个过滤器。Elasticsearch 主动缓存了这些字节集留作以后使用。一旦缓存后,当遇到相同的过滤时,这些字节集就可以被重用,而不需要重新运算整个过滤。

缓存的字节集很“聪明”:他们会增量更新。你索引中添加了新的文档,只有这些新文档需要被添加到已存的字节集中,而不是一遍遍重新计算整个缓存的过滤器。过滤器和整个系统的其他部分一样是实时的,你不需要关心缓存的过期时间。

独立的过滤缓存

每个过滤器都被独立计算和缓存,而不管它们在哪里使用。如果两个不同的查询使用相同的过滤器,则会使用相同的字节集。同样,如果一个查询在多处使用同样的过滤器,只有一个字节集会被计算和重用。

让我们看一下示例,查找符合下列条件的邮箱:

  • 在收件箱而且没有被读取过
  • 不在收件箱但是被标记为重要
"bool": {
   "should": [
      { "bool": {
            "must": [
               { "term": { "folder": "inbox" }}, <1>
               { "term": { "read": false }}
            ]
      }},
      { "bool": {
            "must_not": {
               "term": { "folder": "inbox" } <1>
            },
            "must": {
               "term": { "important": true }
            }
      }}
   ]
}

<1> 这两个过滤器相同,而且会使用同一个字节集。

虽然一个收件箱条件是 must 而另一个是 must_not,这两个条件本身是相等的。这意味着字节集会在第一个条件执行时计算一次,然后作为缓存被另一个条件使用。而第二次执行这条查询时,收件箱的过滤已经被缓存了,所以两个条件都能使用缓存的字节集。

这与查询 DSL 的组合型紧密相关。移动过滤器或在相同查询中多处重用相同的过滤器非常简单。这不仅仅是方便了开发者 —— 对于性能也有很大的提升

控制缓存

大部分直接处理字段的枝叶过滤器(例如 term)会被缓存,而像 bool 这类的组合过滤器则不会被缓存。

【提示】

枝叶过滤器需要在硬盘中检索倒排索引,所以缓存它们是有意义的。另一方面来说,组合过滤器使用快捷的字节逻辑来组合它们内部条件生成的字节集结果,所以每次重新计算它们也是很高效的。

然而,有部分枝叶过滤器,默认不会被缓存,因为它们这样做没有意义:

脚本过滤器:

脚本过滤器的结果不能被缓存因为脚本的意义对于 Elasticsearch 来说是不透明的。

Geo 过滤器:

定位过滤器(我们会在【geoloc】中更详细的介绍),通常被用于过滤基于特定用户地理位置的结果。因为每个用户都有一个唯一的定位,geo 过滤器看起来不太会重用,所以缓存它们没有意义。

日期范围:

使用 now 方法的日期范围(例如 "now-1h"),结果值精确到毫秒。每次这个过滤器执行时,now 返回一个新的值。老的过滤器将不再被使用,所以默认缓存是被禁用的。然而,当 now 被取整时(例如,now/d 取最近一天),缓存默认是被启用的。

有时候默认的缓存测试并不正确。可能你希望一个复杂的 bool 表达式可以在相同的查询中重复使用,或你想要禁用一个 date 字段的过滤器缓存。你可以通过 _cache 标记来覆盖几乎所有过滤器的默认缓存策略

{
    "range" : {
        "timestamp" : {
            "gt" : "2014-01-02 16:15:14" <1>
        },
        "_cache": false <2>
    }
}

<1> 看起来我们不会再使用这个精确时间戳

<2> 在这个过滤器上禁用缓存

以后的章节将提供一些例子来说明哪些时候覆盖默认缓存策略是有意义的。