騰訊雲Elasticsearch集羣規劃及性能優化實踐

​1、引言

 

隨着騰訊雲 Elasticsearch 雲產品功能愈來愈豐富,ES 用戶愈來愈多,雲上的集羣規模也愈來愈大。咱們在平常運維工做中也常常會遇到一些因爲前期集羣規劃不到位,致使後期業務增加集羣規模大了以後帶來的各類各樣的集羣可用性及穩定性問題。node

 

這裏列舉下其中比較典型的幾種集羣規劃問題:python

 

  • 節點規格規劃問題:集羣數量很大,可是每一個節點的配置很低;nginx

  • 索引分片規劃問題:索引很小,可是設置了幾十個分片,或者索引很大,只設置了兩三個分片;sql

  • 分片數量規劃問題:集羣中包含 10萬+ 的分片。性能優化

 

正所謂磨刀不誤砍柴工,只有前期作好充分的集羣評估規劃工做,後期才能省去大量的運維工做。且可以長期保證集羣的高可用和高穩定性。bash

 

本文結合咱們在給騰訊雲 ES 集羣平常運維工做中遇到的各類集羣問題及總結沉澱的一些運維經驗,來介紹下如何規劃好集羣容量及索引配置,以及所遵循的一些原則和經驗。文章做者:吳容,騰訊雲Elasticsearch研發工程師。網絡

 

2、集羣規模及索引規劃

 

 

1. 集羣規模評估

 

(1)評估什麼?

 

集羣規模的評估主要評估如下三個方面:數據結構

 

第一,計算資源評估,計算資源的評估主要是評估單節點的CPU和內存。架構

 

ES的計算資源通常消耗在寫入和查詢過程,通過總結大量ES集羣的運維經驗,2C8G 的配置大概能支持 5k doc/s 的寫入,32C64G 的配置大概能支撐 5w doc/s的寫入能力。併發

 

第二,存儲資源評估,存儲資源的評估主要是評估磁盤的類型及容量大小。

 

例如ES集羣使用什麼類型的磁盤,SSD或者高性能雲盤。以及每塊盤的容量大小,是選擇單盤多容量,仍是多盤少容量。而對於冷熱分離的集羣,則默認使用SSD做爲熱節點,高性能雲盤做爲溫節點。

 

另外騰訊雲ES支持單節點掛載多塊雲硬盤,且通過性能壓測,3塊盤相比於1塊盤,吞吐量大約有2.8倍的提高。所以若是對寫入速度及IO性能要求較高,可選擇掛載多塊 SSD 磁盤。

 

ES冷熱分離多盤集羣示意圖

 

第三,節點數量評估,節點數量的評估主要是評估集羣數據節點的數量。

 

在同等集羣性能的狀況下,建議優先選擇高配置少節點的集羣。例如 32C64G*3 節點的集羣相比於 8C16G*12 節點的集羣,在集羣穩定性和擴容的便捷性上都有必定的優點。

 

由於高配置的集羣若是遇到性能瓶頸須要擴容,則只須要橫向擴容,即向集羣中加入更多同等配置的節點便可;而低配置的集羣在擴容節點配置時,則須要縱向擴容。

 

目前雲上的縱向擴容方式有兩種:

 

第一種是滾動重啓方式擴容,這對集羣穩定性會有必定的影響。

 

第二種是數據遷移方式擴容,其原理是先向集羣中加入同等數量的高配置節點,而後將低配置節點上的數據遷移到新節點上,最後再將低配置節點剔除集羣,因此這種擴容流程時間會比較長,且成本較高。

 

數據遷移方式縱向擴容示意圖

 

(2)根據什麼評估?

 

集羣規模評估主要根據如下三點來評估:

 

  • 具體的業務場景,如日誌分析、指標監控、搜索業務;

  • 業務預計的查詢及寫入QPS;

  • 索引的數據總量。

 

(3)集羣規模評估準則

 

這裏結合咱們的運維經驗,給出集羣規模評估的幾點參考建議:

 

  • 32C64G單節點配置一般可承載5W次/s的寫入;

  • 寫入量和數據量較大時,優先選擇32C64G的節點配置;

  • 1T的數據量預計需消耗2-4GB的內存空間;

  • 搜索場景優先選擇大內存節點配置;

  • 存儲容量 = 源數據 * (1 + 副本數量) * 1.45 * (1 + 預留空間) ≈ 源數據 * (1 + 副本數量) * 2.2.

 

2. 索引配置評估

 

(1)評估什麼?

 

索引配置的評估主要評估兩點:

 

第一,如何劃分索引?

 

在使用 index 時,建議作好按期切換索引的計劃。對於日誌場景來講,寫入不大的狀況下建議按天建立索引,而寫入較大的狀況下,則建議按小時建立索引。

 

按期滾動索引的好處主要包括:可以控制單個索引的大小,提高讀寫性能;同時可以方式單個索引太大,影響故障恢復的時間;另外也能避免熱索引過大,從而影響快照備份恢復的時間。

 

第二,如何設置索引主分片數?

 

雲上的索引主分片數默認是5個,具體的大小則須要業務根據具體的場景及數據量來優化。下面會給出具體的一些準則和經驗。

 

(2)根據什麼評估?

 

索引配置的評估一樣也要結合具體的業務場景及索引的數據量來評估,尤爲是單日新增的數據量。

 

(3)索引配置評估準則

 

索引配置的評估可根據下面幾點準則進行評估:

 

  • 單個分片大小控制在 30-50GB;

  • 集羣總分片數量控制在 3w 之內;

  • 1GB 的內存空間支持 20-30 個分片爲佳;

  • 一個節點建議不超過 1000 個分片;

  • 索引分片數量建議和節點數量保持一致;

  • 集羣規模較大時建議設置專用主節點;

  • 專用主節點配置建議在 8C16G 以上;

  • 若是是時序數據,建議結合冷熱分離+ILM 索引生命週期管理。

 

特別須要說明的是集羣分片總數的大小控制上,咱們通過一些性能測試發現:當集羣的總分片數超過 10w 個之後,建立索引時間會增加到分鐘級。

 

尤爲是對於寫入量在百萬 qps 以上的集羣,若是總分片數在 10W+,且索引是自動建立的,那麼就常常會在每次切換新索引時候,出現寫入陡降、集羣不可用的狀況。

 

下面這張圖是雲上一個 100個節點,總分片數在 11W+ 的集羣。天天 8點切換新索引時,寫入直接掉0,集羣不可用時間在數小時不等。

 

集羣天天8點寫入性能受到影響

 

對於這種問題,咱們騰訊雲ES團隊也有一些很是成熟的優化方案。

 

其中對於天天八點切換新索引時寫入陡降的問題,可經過提早建立索引來解決,且建議使用固定的 index mapping,避免大量的 put-mapping 元數據更新操做。由於對於這種節點數量和總分片數量都很大的集羣來講,更新元數據是一個很是消耗性能的操做。

 

對於總分片數超過 10W 的問題,這種通常在日誌分析場景中較爲常見,若是歷史數據不是很重要,則可按期刪除歷史索引便可。

 

而對於歷史數據較爲重要,任何數據都不能刪除的場景,則可經過冷熱分離架構+索引生命週期管理功能,將7天以前的數據存儲到溫節點,且在索引數據從熱節點遷移到溫節點時,經過 Shrink 來將主分片個數下降到一個較小的值,而且可將溫節點數據經過快照方式備份到騰訊雲COS中,而後將溫節點上索引的副本設置爲0,這樣即可進一步下降集羣中的總分片數量。

 

冷熱分離+ILM+COS備份集羣架構

 

2、ES寫入性能優化


 

ES集羣的寫入性能受到不少因素的影響,下面是一些寫入性能方面的優化建議:

 

1. 寫入數據不指定doc_id,讓 ES 自動生成

 

索引中每個 doc 都有一個全局惟一的 doc_id,這個 doc_id 可自定義,也可讓ES自動生成。

 

若是自定義的話,則ES在寫入過程當中會多一步判斷的過程,即先Get下該 doc_id 是否已經存在。若是存在的話則執行 Update 操做,不存在則建立新的 doc。

 

所以若是咱們對索引 doc_id 沒有特別要求,則建議讓ES自動生成 doc_id,這樣可提高必定的寫入性能。

 

2. 對於規模較大的集羣,建議提早建立好索引,且使用固定的 Index mapping

 

這一條優化建議在上面也提到了,由於建立索引及新加字段都是更新元數據操做,須要 master 節點將新版本的元數據同步到全部節點。

 

所以在集羣規模比較大,寫入qps較高的場景下,特別容易出現master更新元數據超時的問題,這可致使 master 節點中有大量的 pending_tasks 任務堆積,從而形成集羣不可用,甚至出現集羣無主的狀況。

 

更新集羣元數據超時

 

集羣大量pending_tasks任務堆積

 

3. 對於數據實時性要求不高的場景,適當增長 refresh_interval 時間

 

ES默認的 refresh_interval 是1s,即 doc 寫入1s後便可被搜索到。

 

若是業務對數據實時性要求不高的話,如日誌場景,可將索引模版的 refresh_interval 設置成30s,這可以避免過多的小 segment 文件的生成及段合併的操做。

 

4. 對於追求寫入效率的場景,能夠將正在寫入的索引設置爲單副本,寫入完成後打開副本

 

愈來愈多的外部客戶正選擇將自建的ES集羣遷移到騰訊雲上來,客戶一般是使用 logstash 來遷移數據,因爲自建集羣中完整保留了數據,所以這時候能夠將雲上的正在寫入的索引副本設置爲0, 這樣可最快完成集羣遷移工做。數據遷移完成後再將副本打開便可。

 

5. 使用 Bulk 接口批量寫入數據,每次 bulk 數據量大小控制在 10M 左右

 

ES爲了提高寫入性能,提供了 Bulk 批量寫入的API,一般客戶端會準備好一批數據往ES中寫入,ES收到 Bulk 請求後則根據routing 值進行分發,將該批數據組裝成若干分子集,而後異步得發送給各分片所在的節點。

 

這樣可以大大下降寫入請求時的網絡交互和延遲。一般咱們建議一次Bulk的數據量控制在10M如下,一次Bulk的doc數在 10000 上下浮動。

 

ES Bulk請求示意圖

 

6. 使用自定義 routing 功能,儘可能將請求轉發到較少的分片

 

上面咱們提到ES提供了Bulk接口支持將數據批量寫入到索引,雖然協調節點是異步得將數據發送給全部的分片,可是卻須要等待全部的分片響應後才能返回給客戶端,所以一次Bulk的延遲則取決於響應最慢的那個分片所在的節點。這就是分佈式系統的長尾效應。

 

所以,咱們能夠自定義 routing 值,將一次Bulk儘可能轉發到較少的分片上。  

POST _bulk?routing=user_id

 

自定義routing

 

7. 儘可能選擇 SSD 磁盤類型,而且可選擇掛載多塊雲硬盤

 

雲上目前提供多種類型的磁盤可用選擇,其中1T的 SSD 雲盤吞吐量爲 260M/s,高性能雲盤爲 150M/s。所以使用SSD磁盤對於寫入性能和IO性能都會有必定的提高。

 

另外騰訊雲如今也提供了多盤的能力,相對於單盤節點來講,3塊盤的吞吐量大約有2.8倍的提高。

 

8. 凍結歷史索引,釋放更多的內存空間

 

咱們知道ES的索引有三種狀態,分別是 Open狀態、Frozen狀態和 Close狀態。以下圖所示:

 

ES索引的三種狀態

 

Open狀態的索引因爲是經過將倒排索引以FST數據結構的方式加載進內存中,所以索引是可以被快速搜索的,且搜索速度也是最快的。

 

可是須要消耗大量的內存空間,且這部份內存爲常駐內存,不會被GC的。1T的索引預計須要消耗2-4GB的JVM堆內存空間。

 

Frozen狀態的索引特色是可被搜索,可是因爲它不佔用內存,只是存儲在磁盤上,所以凍結索引的搜索速度是相對比較慢的。若是咱們集羣中的數據量比較大,歷史數據也不能被刪除,則能夠考慮使用下面的API將歷史索引凍結起來,這樣即可釋放出較多的內存空間。

POST /index_name/_freeze

 

對於凍結索引的搜索,能夠在API中指定 ignore_throttled=false 參數:

 

GET /index_name/_search?ignore_throttled=false { "query": { "match": { "name": "wurong" } } }

 

上面介紹了一些較爲常見的寫入性能優化的建議和經驗,可是更爲高效的優化還須要結合具體的業務場景和集羣規模。

 

3、ES集羣常規運維經驗總結

 

 

1. 查看集羣健康狀態

 

ES集羣的健康狀態分爲三種,分別是Green、Yellow和Red。

 

  • Green(綠色):所有主&副本分片分配成功;

  • Yellow(黃色):至少有一個副本分片未分配成功;

  • Red(紅色):至少有一個主分片未分配成功。

 

咱們能夠經過下面的API來查詢集羣的健康狀態及未分配的分片個數:

 

GET _cluster/health { "cluster_name": "es-xxxxxxx", "status": "yellow", "timed_out": false, "number_of_nodes": 103, "number_of_data_nodes": 100, "active_primary_shards": 4610, "active_shards": 9212, "relocating_shards": 0, "initializing_shards": 0, "unassigned_shards": 8, "delayed_unassigned_shards": 0, "number_of_pending_tasks": 0, "number_of_in_flight_fetch": 0, "task_max_waiting_in_queue_millis": 0, "active_shards_percent_as_number": 99.91323210412148 }

 

其中須要重點關注的幾個字段有 status、number_of_nodes、unassigned_shards 和 number_of_pending_tasks。

 

number_of_pending_tasks 這個字段若是很高的話,一般是因爲 master 節點觸發的元數據更新操做,部分節點響應超時致使的大量的任務堆積。

 

咱們能夠經過下面的API來查看具體有那些 task 須要執行:

 

GET /_cat/pending_tasks
insertOrder timeInQueue priority source
       1685       855ms HIGH     update-mapping [foo][t] 1686 843ms HIGH update-mapping [foo][t] 1693 753ms HIGH refresh-mapping [foo][[t]] 1688 816ms HIGH update-mapping [foo][t]

 

其中 priority 字段則表示該 task 的優先級,翻看 ES 的源碼能夠看到一共有六種優先級:

 

IMMEDIATE((byte) 0), URGENT((byte) 1), HIGH((byte) 2), NORMAL((byte) 3), LOW((byte) 4), LANGUID((byte) 5);

 

2. 查看分片未分配緣由

 

當集羣Red時候,咱們能夠經過下面的API來查看分片未分配的緣由:

 

GET _cluster/allocation/explain

 

查看分片未分配的緣由

 

其中 index和shard 列出了具體哪一個索引的哪一個分片未分配成功。reason 字段則列出了哪一種緣由致使的分片未分配。這裏也將全部可能的緣由列出來:

 

INDEX_CREATED:因爲建立索引的API致使未分配。 CLUSTER_RECOVERED :因爲徹底集羣恢復致使未分配。 INDEX_REOPENED :因爲打開open或關閉close一個索引致使未分配。 DANGLING_INDEX_IMPORTED :因爲導入dangling索引的結果致使未分配。 NEW_INDEX_RESTORED :因爲恢復到新索引致使未分配。 EXISTING_INDEX_RESTORED :因爲恢復到已關閉的索引致使未分配。 REPLICA_ADDED:因爲顯式添加副本分片致使未分配。 ALLOCATION_FAILED :因爲分片分配失敗致使未分配。 NODE_LEFT :因爲承載該分片的節點離開集羣致使未分配。 REINITIALIZED :因爲當分片從開始移動到初始化時致使未分配(例如,使用影子shadow副本分片)。 REROUTE_CANCELLED :做爲顯式取消從新路由命令的結果取消分配。 REALLOCATED_REPLICA :肯定更好的副本位置被標定使用,致使現有的副本分配被取消,出現未分配。

 

detail 字段則列出了更爲詳細的未分配的緣由。下面我會總結下在平常運維工做中常見的幾種緣由。

 

若是未分配的分片比較多的話,咱們也能夠經過下面的API來列出全部未分配的索引和主分片:

GET /_cat/indices?v&health=red

 

3. 常見分片未分配緣由總結

 

(1)磁盤滿了

the node is above the high watermark cluster setting [cluster.routing.allocation.disk.watermark.high=95%], using more disk space than the maximum allowed [95.0%], actual free: [4.055101177689788%]

 

當咱們執行 _cluster/allocation/explain 命令後看到上面的一行語句的話,則能夠判斷是該索引主分片所在的節點磁盤滿了。

 

解決方法:擴容磁盤提高磁盤容量或者刪除歷史數據釋放磁盤空間。

 

一般若是磁盤滿了,ES爲了保證集羣的穩定性,會將該節點上全部的索引設置爲只讀。ES 7.x版本以後當磁盤空間提高後可自動解除,可是7.x版本以前則須要手動執行下面的API來解除只讀模式:

 

PUT index_name/_settings { "index": { "blocks": { "read_only_allow_delete": null } } }

(2)分片的文檔數超過了21億條限制

 

failure IllegalArgumentException[number of documents in the index cannot exceed 2147483519

 

該限制是分片維度而不是索引維度的。所以出現這種異常,一般是因爲咱們的索引分片設置的不是很合理。

 

解決方法:切換寫入到新索引,並修改索引模版,合理設置主分片數。

 

(3)主分片所在節點掉線

 

cannot allocate because a previous copy of the primary shard existed but can no longer be found on the nodes in the cluster

 

這種狀況一般是因爲某個節點故障或者因爲負載較高致使的掉線。

 

解決方法:找到節點掉線緣由並從新啓動節點加入集羣,等待分片恢復。

 

(4)索引所需屬性和節點屬性不匹配

 

node does not match index setting [index.routing.allocation.require] filters [temperature:\"warm\",_id:\"comdNq4ZSd2Y6ycB9Oubsg\"]

 

解決方法:從新設置索引所需的屬性,和節點保持一致。由於若是從新設置節點屬性,則須要重啓節點,代價較高。

 

例如經過下面的API來修改索引所須要分配節點的溫度屬性:

 

PUT /index_name/_settings
{
  "index": { "routing": { "allocation": { "require": { "temperature": "warm" } } } } }

 

(5)節點長時間掉線後從新加入集羣,引入了髒數據

 

cannot allocate because all found copies of the shard are either stale or corrupt

 

解決方法:經過reroute API來從新分配一個主分片:

 

POST _cluster/reroute?pretty" -d '{ "commands" : [ { "allocate_stale_primary" : { "index" : "article", "shard" : 1, "node" : "98365000222032", "accept_data_loss": true } } ] }

 

(6)未分配分片太多,達到了分片恢復的閾值,其餘分片排隊等待

 

reached the limit of incoming shard recoveries [2], cluster setting [cluster.routing.allocation.node_concurrent_incoming_recoveries=2] (can also be set via [cluster.routing.allocation.node_concurrent_recoveries])

 

這種狀況一般出如今集羣重啓,或者某一個節點重啓後。且因爲設置的分片併發恢復的值較低致使。爲了儘快恢復集羣健康狀態。

 

解決方法:能夠經過調用下面的API來提高分片恢復的速度和併發度:

 

PUT /_cluster/settings
{
    "transient" : { "cluster.routing.allocation.node_concurrent_recoveries": "20", "indices.recovery.max_bytes_per_sec": "100mb" } }

 

結語

 

本文介紹了集羣規模和索引配置規劃的評估準則,依據這些準則提早規劃集羣,能夠保證集羣的穩定性和可用性,簡化複雜的運維工做。

 

另外介紹了一些常見的寫入性能優化的建議和方法。可以進一步提高集羣的寫入性能和穩定性。最後介紹了平常運維工做中常見的排查集羣問題的方法和思路。但願本文可以幫助到騰訊雲的每個ES客戶。

相關文章
相關標籤/搜索