ES系列9、ES优化聚合查询之深度优先和广度优先

1.优化聚合查询示例

假设咱们如今有一些关于电影的数据集,每条数据里面会有一个数组类型的字段存储表演该电影的全部演员的名字。
{
  "actors" : [
    "Fred Jones",
    "Mary Jane",
    "Elizabeth Worthing"
  ]
}

     若是咱们想要查询出演影片最多的十个演员以及与他们合做最多的演员,使用聚合是很是简单的:数组

     {
  "aggs" : {
    "actors" : {
      "terms" : {
         "field" : "actors",
         "size" :  10
      },
      "aggs" : {
        "costars" : {
          "terms" : {
            "field" : "actors",
            "size" :  5
          }
        }
      }
    }
  }
}

 

     这会返回前十位出演最多的演员,以及与他们合做最多的五位演员。这看起来是一个简单的聚合查询,最终只返回 50 条数据!
     可是, 这个看上去简单的查询能够垂手可得地消耗大量内存,咱们能够经过在内存中构建一个树来查看这个 terms 聚合。 actors 聚合会构建树的第一层,每一个演员都有一个桶。而后,内套在第一层的每一个节点之下, costar 聚合会构建第二层,每一个联合出演一个桶。这意味着每部影片会生成 n2 个桶!
 

2.深度优先和广度优先原理

     Elasticsearch 容许咱们改变聚合的 集合模式 ,就是为了应对这种情况。 咱们以前展现的策略叫作 深度优先 ,它是默认设置, 先构建完整的树,而后修剪无用节点。 深度优先 的方式对于大多数聚合都能正常工做,但对于如咱们演员和联合演员这样例子的情形就不太适用。
     为了应对这些特殊的应用场景,咱们应该使用另外一种集合策略叫作 广度优先 。这种策略的工做方式有些不一样,它先执行第一层聚合, 再 继续下一层聚合以前会先作修剪。
 
     在咱们的示例中, actors 聚合会首先执行,在这个时候,咱们的树只有一层,但咱们已经知道了前 10 位的演员!这就没有必要保留其余的演员信息,由于它们不管如何都不会出如今前十位中。由于咱们已经知道了前十名演员,咱们能够安全的修剪其余节点。修剪后,下一层是基于 它的 执行模式读入的,重复执行这个过程直到聚合完成。
     要使用广度优先,只需简单 的经过参数 collect 开启:
{
  "aggs" : {
    "actors" : {
      "terms" : {
         "field" :        "actors",
         "size" :         10,
         "collect_mode" : "breadth_first"
      },
      "aggs" : {
        "costars" : {
          "terms" : {
            "field" : "actors",
            "size" :  5
          }
        }
      }
    }
  }
}

 

      广度优先仅仅适用于每一个组的聚合数量远远小于当前总组数的状况下,由于广度优先会在内存中缓存裁剪后的仅仅须要缓存的每一个组的全部数据,以便于它的子聚合分组查询能够复用上级聚合的数据
     广度优先的内存使用状况与裁剪后的缓存分组数据量是成线性的。对于不少聚合来讲,每一个桶内的文档数量是至关大的。 想象一种按月分组的直方图,总组数确定是固定的,由于每一年只有12个月,这个时候每月下的数据量可能很是大。这使广度优先不是一个好的选择,这也是为何深度优先做为默认策略的缘由。
     针对上面演员的例子,若是数据量越大,那么默认的使用深度优先的聚合模式生成的总分组数就会很是多,可是预估二级的聚合字段分组后的数据量相比总的分组数会小不少因此这种状况下使用广度优先的模式能大大节省内存,从而经过优化聚合模式来大大提升了在某些特定场景下聚合查询的成功率。
相关文章
相关标签/搜索