騰訊雲Elasticsearch叢集規劃及效能優化實踐

騰訊雲+社群發表於2020-09-30

​一、引言

 

隨著騰訊雲 Elasticsearch 雲產品功能越來越豐富,ES 使用者越來越多,雲上的叢集規模也越來越大。我們在日常運維工作中也經常會遇到一些由於前期叢集規劃不到位,導致後期業務增長叢集規模大了之後帶來的各種各樣的叢集可用性及穩定性問題。

 

這裡列舉下其中比較典型的幾種叢集規劃問題:

 

  • 節點規格規劃問題:叢集數量很大,但是每個節點的配置很低;

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

  • 分片數量規劃問題:叢集中包含 10萬+ 的分片。

 

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

 

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

 

二、叢集規模及索引規劃

 

 

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備份叢集架構

 

二、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"
   }
 }
}

 

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

 

三、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客戶。

相關文章