Shard 的數量與 Rollover & Shrink API

前言

當我們將資料 Indexing 進入 Elasticsearch 後,隨著時光的飛逝,我們 Index 裡的資料通常也會愈來愈多,這時如何有效的管理資料就是一件很重要的事,這個主題的文章會來介紹 Index 管理的 Best Practices,這篇文章會著重在 Index 中 Shard 的數量配置方式及優化的方法。

進入此章節的先備知識

  • 知道什麼是 Elasticsearch Index、Alias、Index Template。

  • 初步了解 Lucene, Shard, Segment Files。

此章節的重點學習

  • Shard 的數量該怎麼設定。

  • 隨著時間的變化,如何透過 Rollover API 及 Shrink API 來優化 Elasticsearch 中的 Indices。


Shard 的重要觀念

在一開始我們說明一下 Shard 的幾個重要的觀念:

  • Shard 是 ES Cluster 中切分資料來儲存的最小單位,裡面是透過 Lucene 管理的一群 Segment Files,也就是 Lucene Index。

  • Shard 的大小與數量:

    • 最大多大?: 沒有一定的限制,主要是依照 Elasticsearch Cluster 硬體的性能來決定,早期官方文件是寫建議 10G,但忘了從哪個版本開始(應該有二、三年前),文件上已改成 20~40G,隨著硬體規格的成長、Elasticsearch 版本演進時效能不斷的優化,這個數字會需要實際測試才知道,以我實際的經驗,200G以上的 shard 也有遇到過。

    • 最小多小?: 如果要設多個 shard 的話,最好不要小於 1G,否則這時候切 shard 的好處太少,不如不要切 shard。

    • 數量多的好處?: 數量愈多的話,好處是資料量非常大時可以分散到多個 Node 去儲存、又或是有大量的 Indexing 的需求可以讓多個 Node 去分擔,若是單一 Shard 的儲存空間足夠,又沒有大量 Indexing 的需求時,應該讓 Shard 大一些,並且數量少一點會比較好。

    • 數量少的好處?: 數量愈少其實處理搜尋的效能會較好,但取捨是當 Cluster 要 Rebalancing 時,一個巨大的 Shard 要搬移的成本很高。

Index 的管理方式

若你的資料量是固定的、或是成長非常緩慢的,請直接依照上述 shard 的觀念、硬體規格、成長規劃來設定 shard 的數量,以下著重在介紹隨著時間成長的資料的 Index 管理方式。

隨著時間增長的資料,資料要能分散存取的話,官方建議使用 time-based indices 來進行資料的管理,而不是在一開始設定大量的 shard 數量來期待之後資料的成長,因為 shard 的數量在建立 Index 時設定好之後,就不能再修改。

Elasticsearch 6.x 版之前,預設的 shard 數量是 5,但是從 7.x 版開始,預設的 shard 數量已改成 1,官方建議使用 time-based indices 來處理隨時間成長的資料。

Index 的數量太多的成本?

Index 的資訊是存在 Cluster stats 中,如果 Index 數量太多 (特別是 Mapping 欄位又很多時),會讓 Cluster stats 變很大,這會佔用 JVM heap size,也會造成 update 這種需透用到 cluster stats 資訊來確保處理一致性的請求效能變慢。

針對 time-based indices 的管理方式 - Rollover Pattern

一般這種資料我們的期待與使用情境如下:

  • 在 indexing 大量資料時,為了有較好的效能,我們一開始會將 shard 數量提高。

  • 當資料趨近穩定、不太會變動時,我們為了要有更好的查詢效能,我們會希望 shard 的數量愈少愈好,但也不要到太肥大的狀態。

  • 有時可能覺得一天一個 Index 會比較好照時間來管理過期的資料,但每天的資料量可能不一定,Shard 的數量又不見得都適合套用同一套規則。

因此這時官方的 Rollover & Shrink 會是很好的解決方案,這個 Rollover Pattern 的基本運作如下:

  • 定一個 indexing 新文件專用的 Alias ,並將他指到目前 active index。

  • 定另一個 searching 用的 Alias,指定所有不論新、舊的 indices。

  • active index 可以指定有很多個 shard,讓寫入的效能能最佳。

  • 當 active index 資料量達到一個條件、或是時間過太久了,會進行 Rollover,產生一個新的 index 成為 active index,並把 indexing 用的 alias 指向他。

  • 舊的 index 被搬到 code node,並觸發 Shrink 的動作,將他轉成單一 shard 的 index,並且觸發 forced-marged 和 compression ,以進行資源的優化。

以下分別針對 Rollover API 及 Shrink API 來示範 Rollover Pattern 的實作:

Rollover

建立一個 index - logs-000001 與指向他的 alias - logs_write

PUT /logs-000001 
{
  "aliases": {
    "logs_write": {}
  }
}

使用 Rollover API 建立 超過7天滿1000筆文件 要觸發自動 rollover:

POST /logs_write/_rollover 
{
  "conditions": {
    "max_age":   "7d",
    "max_docs":  1000
  }
}

Rollover 的規則,會依照 index 名字後有 - 而且接著數字,就會接受這是要 Rollover 的 index,但長出來新的 index 會依照這邊的定義,會是六位數並且會補 0 的格式。

例如當筆數滿 1000 後,會產生新的 index logs-000002

若新的 index 不是這個名命的規則,可以明確的指定名字。

POST /my_alias/_rollover/my_new_index_name
{
  "conditions": {
    "max_age":   "7d",
    "max_docs":  1000
  }
}

Shrink

Shrink 的 API 主要的目的就是把 index 的 shard 數量變少,而變少的規則,必需是原來數值的因數,例如原本是 8 ,那只能 shrink 成 4 或 2 或 1、原本是 15 的話,只能 shrink 成 5 或 3 或 1。

運作機制如下:

  • 建立一個新的 index,其設定會完全照之前的 index,只有 shard 數量是變少的,此時 index 的狀態會先保持 _close

  • 透過 OS 的 hard-links 來合併 shards ,如果 OS file system 不支援,會直接用複製的方式將 shard 複製到新的 index 裡。

  • 最後再將新的 index 重新 open。

Shrink API 如下:

POST my_source_index/_shrink/my_target_index

另外也可以宣告一些類似 create index API 能指定的參數:

POST my_source_index/_shrink/my_target_index
{
  "settings": {
    "index.number_of_replicas": 1,
    "index.number_of_shards": 1, 
    "index.codec": "best_compression" 
  },
  "aliases": {
    "my_search_indices": {}
  }
}

透過這些設定,就能指定 shrink 成特定數量的 shards 了。 (不過要記得必須是原來 shard 數量的因數才行。)

參考資料

Last updated