Elasticsearch 分片恢復併發數過大引發的bug分析

       大家好,今天爲大家分享一次 ES 的填坑經驗。主要是關於集羣恢復過程中,分片恢復併發數調整過大導致集羣 hang 住的問題。

場景描述

       廢話不多說,先來描述場景。某日,騰訊雲線上某 ES 集羣,15個節點,2700+ 索引,15000+ 分片,數十 TB 數據。由於機器故障,某個節點被重啓,此時集羣有大量的 unassigned 分片,集羣處於 yellow 狀態。爲了加快集羣恢復的速度,手動調整分片恢復併發數,原本想將默認值爲2的 node_concurrent_recoveries 調整爲10,結果手一抖多加了一個0,設定了如下參數:

curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
    "persistent": {
        "cluster.routing.allocation.node_concurrent_recoveries": 100,
        "indices.recovery.max_bytes_per_sec": "40mb"
    }
}
'

       設定之後,觀察集羣 unassigned 分片,一開始下降的速度很快。大約幾分鐘後,數量維持在一個固定值不變了,然後,然後就沒有然後了,集羣所有節點 generic 線程池卡死,雖然已存在的索引讀寫沒問題,但是新建索引以及所有涉及 generic 線程池的操作全部卡住。立馬修改分片恢復併發數到10,通過管控平臺一把重啓了全部節點,約15分鐘後集羣恢復正常。接下來會先介紹一些基本的概念,然後再重現這個問題並做詳細分析。

基本概念

ES 線程池(thread pool)

ES 中每個節點有多種線程池,各有用途。重要的有:

  • generic :通用線程池,後臺的 node discovery,上述的分片恢復(node recovery)等等一些通用後臺的操作都會用到該線程池。該線程池線程數量默認爲配置的處理器數量(processors)* 4,最小128,最大512。
  • index :index/delete 等索引操作會用到該線程池,包括自動創建索引等。默認線程數量爲配置的處理器數量,默認隊列大小:200.
  • search :查詢請求處理線程池。默認線程數量:int((# of available_processors * 3) / 2) + 1,默認隊列大小:1000.
  • get :get 請求處理線程池。默認線程數量爲配置的處理器數量,默認隊列大小:1000.
  • write :單個文檔的 index/delete/update 以及 bulk 請求處理線程。默認線程數量爲配置的處理器數量,默認隊列大小:200,在寫多的日誌場景我們一般會將隊列調大。 還有其它線程池,例如備份回檔(snapshot)、analyze、refresh 等,這裏就不一一介紹了。詳細可參考官方文檔:https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-threadpool.html

集羣恢復之分片恢復

       我們知道 ES 集羣狀態分爲三種,green、yellow、red。green 狀態表示所有分片包括主副本均正常被分配;yellow 狀態表示所有主分片已分配,但是有部分副本分片未分配;red 表示有部分主分片未分配。

一般當集羣中某個節點因故障失聯或者重啓之後,如果集羣索引有副本的場景,集羣將進入分片恢復階段(recovery)。此時一般是 master 節點發起更新集羣元數據任務,分片的分配策略由 master 決定,具體分配策略可以參考騰訊雲+社區的這篇文章瞭解細節:https://cloud.tencent.com/developer/article/1334743 。各節點收到集羣元數據更新請求,檢查分片狀態並觸發分片恢復流程,根據分片數據所在的位置,有多種恢復的方式,主要有以下幾種:

  • EXISTING_STORE : 數據在節點本地存在,從本地節點恢復。
  • PEER :本地數據不可用或不存在,從遠端節點(源分片,一般是主分片)恢復。
  • SNAPSHOT : 數據從備份倉庫恢復。
  • LOCAL_SHARDS : 分片合併(縮容)場景,從本地別的分片恢復。

PEER 場景分片恢復併發數主要由如下參數控制:

  • cluster.routing.allocation.node_concurrent_incoming_recoveries :節點上最大接受的分片恢復併發數。一般指分片從其它節點恢復至本節點。
  • cluster.routing.allocation.node_concurrent_outgoing_recoveries :節點上最大發送的分片恢復併發數。一般指分片從本節點恢復至其它節點。
  • cluster.routing.allocation.node_concurrent_recoveries :該參數同時設置上述接受發送分片恢復併發數爲相同的值。 詳細參數可參考官方文檔:https://www.elastic.co/guide/en/elasticsearch/reference/current/shards-allocation.html

       集羣卡住的主要原因就是從遠端節點恢復(PEER)的併發數過多,導致 generic 線程池被用完。涉及目標節點(target)和源節點(source)的恢復交互流程,後面分析問題時我們再來詳細討論。

問題復現與剖析

       爲了便於描述,我用 ES 6.4.3版本重新搭建了一個三節點的集羣。單節點 1 core,2GB memory。新建了300個 index, 單個 index 5個分片一個副本,共 3000 個 shard。每個 index 插入大約100條數據。

先設定分片恢復併發數,爲了誇張一點,我直接調整到200,如下所示:

curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
    "persistent": {
        "cluster.routing.allocation.node_concurrent_recoveries": 200 // 設定分片恢復併發數
    }
}
'

接下來停掉某節點,模擬機器掛掉場景。幾分鐘後,觀察集羣分片恢復數量,卡在固定數值不再變化:

分片恢復統計信息

通過 allocation explain 查看分片分配狀態,未分配的原因是受到最大恢復併發數的限制:

分片恢復限制

觀察線程池的數量,generic 線程池打滿128.

線程池統計

此時查詢或寫入已有索引不受影響,但是新建索引這種涉及到 generic 線程池的操作都會卡住。

通過堆棧分析,128 個 generic 線程全部卡在 PEER recovery 階段。

堆棧分析

       現象有了,我們來分析一下這種場景,遠程分片恢復(PEER Recovery)流程爲什麼會導致集羣卡住。

       當集羣中有分片的狀態發生變更時,master 節點會發起集羣元數據更新(cluster state update)請求給所有節點。其它節點收到該請求後,感知到分片狀態的變更,啓動分片恢復流程。部分分片需要從其它節點恢復,代碼層面,涉及分片分配的目標節點(target)和源節點(source)的交互流程如下:

遠端恢復時序分析

       6.x 版本之後引入了 seqNo,恢復會涉及到 seqNo+translog,這也是6.x提升恢復速度的一大改進。我們重點關注流程中第 2、4、5、7、10、12 步驟中的遠程調用,它們的作用分別是:

  • 第2步:分片分配的目標節點向源節點(一般是主分片)發起分片恢復請求,攜帶起始 seqNo 和 syncId。
  • 第4步:發送數據文件信息,告知目標節點待接收的文件清單。
  • 第5步:發送 diff 數據文件給目標節點。
  • 第7步:源節點發送 prepare translog 請求給目標節點,等目標節點打開 shard level 引擎,準備接受 translog。
  • 第10步:源節點發送指定範圍的 translog 快照給目標節點。
  • 第12步:結束恢復流程。

       我們可以看到除第5步發送數據文件外,多次遠程交互 submitRequest 都會調用 txGet,這個調用底層用的是基於 AQS 改造過的 sync 對象,是一個同步調用。 如果一端 generic 線程池被這些請求打滿,發出的請求等待對端返回,而發出的這些請求由於對端 generic 線程池同樣的原因被打滿,只能 pending 在隊列中,這樣兩邊的線程池都滿了而且相互等待對端隊列中的線程返回,就出現了分佈式死鎖現象。

問題處理

       爲了避免改動太大帶來不確定的 side effect,針對騰訊雲 ES 集羣我們目前先在 rest 層拒掉了併發數超過一定值的參數設定請求並提醒用戶。與此同時,我們向官方提交了 issue:https://github.com/elastic/elasticsearch/issues/36195 進行跟蹤。

總結

       本文旨在描述集羣恢復過程出現的集羣卡死場景,避免更多的 ES 用戶踩坑,沒有對整體分片恢復做詳細的分析,大家想了解詳細的分片恢復流程可以參考騰訊雲+社區的 Elasticsearch 實驗室專欄:https://cloud.tencent.com/developer/column/2428

完結,謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章