Elasticsearch Tips for log data

從 Elasticsearch 1.0 到現在也斷斷續續的玩了快一年了,主要都是在處理 log 類型的資料,記錄一下一些心得。

Terminology

  • Elasticsearch: 以 Apache Lucene 為底層的搜尋引擎,採用者有 Wikipedia,Github 等
  • Elasticsearch index: 為了簡化,可以想像成 RDBMS 裡的 database (實際上差很多),由數個 shard 所組成
  • shard: 每個 shard 就是一個 Lucene index,為Elasticsearch 中最小的工作單位
  • Lucene index: 由許多的 immutable segments 所組成,每個 segment 裡面是許多的 SSTable
  • index (v.): 做為動詞,index 本身指的是 insert 資料到 Elasticsearch

Cluster/Node Configuration

以下是幾個非常重要的設定,即使有的可以動態透過 REST API 更改,通常還是會寫在 /etc/elasticsearch/elasticsearch.yml 裡:

# /etc/elasticsearch/elasticsearch.yml

cluster.name: cluster_name

node.name: node_name

path.data: /path/to/data/1,/path/to/data/2

bootstrap.mlockall: true

discovery.zen.minimum_master_nodes: floor(n+1)/2

discovery.zen.ping.multicast.enabled: false
  • cluster.name: 不論使用哪種 discovery 方式,只有同樣的 cluster name 的才會形成一個 cluster。
  • node.name: 不指定的話,每次 node 重啟會得到一個新的名字。 (而且還是 Marvel 角色的名字!)
  • path.data: 資料存放的路徑,可以指定多個,類似 RAID 0,未來也可以再增減。
  • bootstrap.mlockall: 防止 memory 被 swap 掉,詳細設定請參考官網
  • discovery.zen.minimum_master_nodes: n 為 cluster 中 master node 的數量,例如 5 個 master nodes 就設定為 3。
  • discovery.zen.ping.multicast.enabled: 無論使用 unicast 或者 aws/azure 都要 disable multicast 以防止加入預期以外的 cluster/node。

另外有一些則會寫在 /etc/default/elasticsearch

  ES_HEAP_SIZE = 2G
  MAX_LOCKED_MEMORY = unlimited
  MAX_MAP_COUNT = 262144

ES_HEAP_SIZE: Elasticsearch 需要大量的 memory,但由於 Lucene 會大量的使用 OS cache,所以 HEAP_SIZE 通常設定在 50% 的記憶體,剩下的 50% 留給 OS 利用。

除了這個限制以外,如果你的 ES_HEAP_SIZE 算出來介於 32GB~50GB 之間,官方的建議是一樣設定在 32GB,詳情可以看這裡

Tips

1.避免 over-sharding

由於 shard 的數量是不能改變的,所以有時會為了保留 scale out 的可能性,而設定 1000 個shard。然而每個 shard 相當於一個 Lucene index,過多會帶來很大的 overhead。合理的數字是 (# of nodes) ~ (# of nodes) * 2。

2.以時間切 index

例如把 nginx access log 存成 nginx-access-YYYY-MM-DD。這樣做的好處是保持未來更動 mapping 的彈性,同時也可以把資料分成 hot/warm/cold,例如:

  • hot data: open index
  • warm data: close index
  • cold data: snapshot in S3

另外如果有使用 kibana,它也會只對查詢選取時間範圍內的 index,而不是查詢整個 nginx-access。

如果常使用 aggregation,發現 memory 一直被 field data cache 吃完,很可能就是沒有適當的把資料切開。

3.了解資料的 mapping

雖然 Elasticsearch 不需事先定義 mapping (也就是 schema-less),但其實每個 index 還是會根據輸入的資料,去得出 mapping。

以 log 的資料為例,通常有以下的特性:

  • ip: 型態為字串,不需要切字,所以應該設定為 string, not_analyzed
  • time-elasped: 型態為 float
  • path: 型態為字串,適合的 tokenizer 為 path hierarchy tokenizer

這些設定其實都是不會變的,先定義好在建立 index 的時候會比較快。

另外如果大量使用 aggregation,也可以額外再設定 doc_value

至於如何讓每天新建立的 index 都設定好,請接著看下一點。

4.善用 index template

index template 可以指定 match 特定的 index 名稱,例如 nginx-access-*,設定的方式相當彈性,例如 nginx-access-2015-0101 同時 match 了 nginx-access-** 兩個 template,Elasticsearch 會 deep merge 兩個 template。

另外常用的設定是 num_of_shards, num_of_replicas。

5.定期 optimize index

Lucene 的 index 是由一堆 immutable, append-only 的 segments 所組成,在寫入的過程中,會不斷的 merge 成更大的 segment,請直接看這個動畫

由於 log 資料的特性就是不會寫過去的 index,所以可以把過去的 index 都盡可能的 merge (每個 shard 只剩下一個 segment),而 Elasticsearch 就提供了 Optimize API 來完成此任務。

6.善用 Curator + crontab

Curator 是 Elasticsearch 放在 Github 上的命令列工具,可以很輕易的做到:

  • optimize 2 天以前的所有 indices
  • 刪除 90 天前的所有 indices
  • snapshot 所有的 indices
  • close 14天前的所有 indices

再配合上 crontab,可以輕易的維持 hot/warm/cold data 的存放。

7.避免過大的 mapping

假設我們要建立一個 index ,裡面有每個人身高的資訊

data:

  {
      "Yao Ming": 226
  },
  {
      "Allen Iverson": 180
  },
  {
      "Kobe Bryant": 196
  }

會產生

  {
      "Yao Ming": "float",
      "Allen Iverson": "float",
      "Kobe Bryant": "float"
  }

的 mapping

如果 data 變成這樣:

  {
      "name": "Yao Ming",
      "height": 226
  },
  {
      "name": "Allen Iverson",
      "height": 180
  },
  {
      "name": "Kobe Bryant",
      "height": 196
  }

則會產生

  {
      "name": "string",
      "height": "float"
  }

的 mapping

以第一種方式記錄資料,會造成 mapping 無止境的增長,而 mapping 每更動一次,所有 cluster 內的 node 都要 sync mapping,而越長越大的 mapping 會導致 sync 的時間越來越長,最後導致 cluster 非常不穩定。

後記

寫了好久終於寫完了,有許多地方寫得不夠完整,也許未來有機會再把重要的幾點單純拉出來寫。
有任何的指教/討論也都十分歡迎
感謝收看 :D

Comments

comments powered by Disqus