1. 程式人生 > >ES優化聚合查詢之深度優先和廣度優先

ES優化聚合查詢之深度優先和廣度優先

策略 字段 示例 pre collect 功率 模式 二層 信息

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個月,這個時候每個月下的數據量可能非常大。這使廣度優先不是一個好的選擇,這也是為什麽深度優先作為默認策略的原因。 針對上面演員的例子,如果數據量越大,那麽默認的使用深度優先的聚合模式生成的總分組數就會非常多,但是預估二級的聚合字段分組後的數據量相比總的分組數會小很多所以這種情況下使用廣度優先的模式能大大節省內存,從而通過優化聚合模式來大大提高了在某些特定場景下聚合查詢的成功率。

ES優化聚合查詢之深度優先和廣度優先