Elasticsearch 集羣優化-儘可能全面詳細

Elasticsearch 集羣優化-轉載參考1

基本配置

  • 基本配置,5臺配置爲 24C 125G 17T 的主機,每臺主機上搭建了一個elasticsearch節點。

採用的elasticsearch集羣版本爲7.1.1。管理工具包括kibana和cerebro。

應用案例

  • 數據來源爲kafka的三個topic,主要用於實時日誌數據的存儲和檢索,由於實時性要求,所以需要將數據快速的寫入到es中。
    這裏就分別稱它們爲TopicA、TopicB、TopicC吧。由於是調優寫入,所以對源數據的一些基本的指標需要作出一個詳細的梳理,便於後續分析。以下爲三個topic的數據產生情況:

file

規劃

Topic 報文大小 數據量預估(天) es索引分片數規劃
topicA 900b 2T 30個主分片
topicB 800b 400G 10個主分片
topicC 750b 300G 10個主分片

問題重現

  • 未做任何配置的情況下,分別使用java和logstash進行數據抽取,發現效率都不高,具體問題表現在:
    • 1、kafka數據積壓嚴重,消費跟不上生產的速度。
    • 2、elasticsearch集羣負載很高,大量寫入被拒絕。
    • 3、java程序頻繁拋出RejectionException異常。
    • 4、主機cpu異常的高。

操作系統層面及JVM的配置調整這裏不再闡述,有很多關於此類的文章可以參考。
我們分模塊對各個部分進行調整,具體細節如下:

寫入程序優化

從定數到定量

  • 使用的java程序中,我們將固定條數插入改爲固定大小插入,由於使用的es版本較高,直接替換成了官方推薦的BulkProcessor方式。具體指定屬性如下:
# 每2w條執行一次bulk插入
bulkActions: 20000
# 數據量達到15M後執行bulk插入
 bulkSizeMb: 15
# 無論數據量多少,間隔20s執行一次bulk
flushInterval: 20
# 允許併發的bulk請求數
concurrentRequests: 10
  • 這裏的具體配置值,可以根據觀察集羣狀態,來逐步增加。對於高版本的es,可以通過x-pack的監控頁面觀察索引速度進行相應調整,如果es版本較低,可以使用推薦的rest api進行邏輯封裝。

在低版本的es中,統計寫入速度的思路是:寫一個程序定時檢查索引的數據量,來計算。如果使用python,就兩行代碼就能獲取索引的數據總量。

 call_list = es.indices.stats(index=index)
total = call_list['indices'][index]['total']['indexing']['index_total']。

也可以隔幾分鐘用CURL來粗略統計單個索引的數據量大小。命令如下:

查詢索引文檔總量

curl -XGET -uname:pwd
'http://esip:port/_cat/count/index-name?v&format=json&pretty'

啓動多個進程

  • 由於Bulkprocess是線程安全的,所以我們可以使用多線程的方式來共享一個批處理器。更好的消費方式是,啓動多個消費程序進程,將其部署在不同的主機上,讓多個進程中開啓的多線程總數和topic的分區數相等,並且將他們設置爲同一個消費組。每一個進程包含一個bulkprocess,可以提高消費和批量寫入能力。同時可以避免單點問題,假如一個消費者進程掛掉,則kafka集羣會重新平衡分區的消費者。少了消費者只是會影響消費速度,並不影響數據的處理。

“壓測”,提升批量插入條數

  • 通過對各個監控指標的觀察,來判斷是否能繼續提高寫入條數或增加線程數,從而達到最大吞吐量。

一、觀察集羣負載Load Average值

  • 負載值,一定程度上代表了CPU的繁忙程度,那我們如何來解讀elasticsearch 監控頁面的的負載值呢?如下是一個三個節點的集羣,從左側cerebo提供的界面來看,load值標紅,表明es的負載可能有點高了,那麼這個具體達到什麼值會顯示紅色呢,讓我們一起來研究研究。

    file

file

  • 先從主機層面說起,linux下提供了一個uptime命令來觀察主機的負載

file

  • 其中load average的三個值,分別代表主機在1分鐘、5分鐘、15分鐘內的一個負載情況。有人可能會疑惑,26.01是代表主機的負載在26%的意思嗎,從我們跑的es集羣情況來看,這顯然不是負載很低的表現。其實啊,在單個cpu的情況下,這個值是可以看做一個百分比的,比如負載爲0.05,表明目前系統的負荷爲5%。但我們的服務器一般都是多個處理器,每個處理器內部會包含多個cpu核心,所以這裏負載顯示的值,是和cpu的核心數有關的,如果非要用百分比來表示系統負荷的話,可以用具體的負載值 除以 服務器的總核心數,觀察是否大於1。總核心數查看的命令爲:
cat /proc/cpuinfo |grep -c 'model name'
  • 這臺主機顯示爲24,從26的負載來看,目前處理的任務需要排隊了,這就是爲什麼負載標紅的原因。同時,這裏列舉一下,如何查看CPU情況
總邏輯CPU數 = 物理CPU個數 X 每顆物理CPU的核數 X 超線程數
# 物理CPU個數(我們的服務器是2個)
cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l


# 查看每個物理CPU中core的個數(就是核數)(6核)
cat /proc/cpuinfo| grep "cpu cores"| uniq

# 查看邏輯CPU的個數
cat /proc/cpuinfo| grep "processor"| wc -l
(顯示24,不等於上面的cpu個數 * 每個cpu的核數,說明是開啓了超線程)

二、觀察集羣在 "忙什麼"

  • 通過tasks api可以直觀的 觀察到集羣在忙什麼?,包括父級任務,任務的持續時間等指標。命令如下:
curl -u username:pwd ip:port/_cat/tasks/?v | more

file

  • 上面是我把副本設置爲0後截的圖。理論上還應該有一個bulks 操作。可以看到目前寫入很耗時,正常情況一批bulk操作應該是毫秒級的,這也從側面說明了es的負載很高。

從task_id、parent_task_id可以看出,一個bulk操作下面分爲寫主分片的動作 和寫副本的動作。其中:
indices:data/write/bulks:s表示分片,p表示主分片。
indices:data/write/bulks:s表示分片,r表示副本。

簡易的寫入流程
  • 如下是bulk請求的簡易寫入流程,我們知道客戶端會選擇一個節點發送請求,這個節點被之稱爲協調節點,也叫客戶端節點,但是在執行之前,如果定義了預處理的pipline操作(比如寫入前將key值轉換,或者增加時間戳等),則此寫操作會被攔截並進行對應邏輯處理。
    • 從圖中可以看出,寫入操作會現根據路由出來的規則,決定發送數據到那個分片上去,默認情況下,是通過數據的文檔id來進行路由的,這能保證數據平均分配到各個節點上去,也可以自定義路由規則,具體定義方式我們在下面會講到。
      • 接着,請求發送到了主分片上,主分片執行成功後,會將請求再轉發給相應的副本分片,在副本分片上執行成功後,這個請求才算是執行完畢,然後將執行結果返回給客戶端。
      • 可以看出多副本在寫多讀少的場景下,十分的消耗性能,近似的,多了幾個副本就相當於重複寫了幾份數據。如果不考慮數據容災,則可以適當的降低副本數量,或者去掉副本,提高寫入速度。

在我們的集羣裏面並沒有用到ingest角色類型的節點,這裏提出來說也是爲了便於大家更好理解各個節點的角色分工。

file

- 通過ES提供的API觀察各個節點的熱線程,api結果會顯示出佔用cpu高的線程,這也是我們可以優化的地方。大量寫入場景下,這裏一般大多數會顯示:Lucene Merge Thread 或者[write],查詢命令爲:
GET /_nodes/hot_threads

三、觀察集羣線程池狀態

  • 避免大量寫入被拒絕,可以通過觀察elasticsearch後臺日誌或是通過使用Thread pool Api來觀察內部線程池的使用情況,以及相應使用的隊列大小,判斷是否還可以繼續調整寫入配置參數。
curl -uusername:pwd-XGET "http://esip:port/_cat/thread_pool?v" | grep write

寫入負載高的情況下,可能會出現大量拒絕,如下:

node-name name active queue
node-3 write 4 0
node-1 write 3 2
node-2 write 9 1

主機部分

每個目錄掛載不同的磁盤

  • 在data目錄下,我們分出了10個子目錄,分別掛載到不同的硬盤上去。這相當於做了raid0。能大大的提高寫入速度。

    配置多個path.data

    • 由於在前面我們將10個目錄分別掛載到不同的硬盤上去,所以在elasticsearch.yml的path.data屬性中,我們配置多個路徑,讓數據能高效的寫入不同的目錄(硬盤),需要注意的是,如果只有一個索引,它的分片在某個節點的存儲目錄是固定的。所以這個特性,也只有在存在多個索引的情況下,能發揮出它的作用。

一個主機啓動兩個節點

  • es實例分配內存不會超過32G,對於主機數量固定的我們,如果125G的機器只放一個es節點,實屬有點浪費,所以考慮在主機上啓動兩個es節點實例。

配置上需要注意關注以下幾點:

- 1、http的端口、節點間通信的trasport端口設置。
- 2、節點的角色分配。
- 3、腦裂配置對應修改。
- 4、path.data屬性修改(重要)
- 5、path.logs屬性修改。

修改path.data配置,使同一主機兩個節點均分硬盤

- 這裏着重說一下第4點,同一個主機啓動兩個實例後,我們將path.data配置從原來的10個目錄改爲了各自配置5個不同目錄。
path.data: /data01/esdata,/data02/esdata,/data03/esdata
,/data04/esdata,/data05/esdata
  • 一方面是 能夠控制分片的分配,避免太多分片分配到一臺主機上的其中一個節點上。另一方面是避免兩個es進程對同一磁盤進行寫入。隨機寫造成的磁頭非常頻繁的大面積移動肯定比單進程的順序寫入慢,這也是我們提高寫入速度的初衷。

更換ssd

  • ssd能成倍的提高寫入速度,如果使用ssd,可能就不會折騰這篇文章出來了。

elasticsearch部分

節點角色的設置

  • elasticsearch提供幾種類型的節點角色設置,需要在elasticsearch.yml配置中指定。

類型。
file

指定索引模板

可以根據需要修改,具體配置含義不再細說。
{
  "order": 0,
  "index_patterns": [
    "topicA*"
  ],
  "settings": {
    "index": {
      "refresh_interval": "40s",
      "number_of_shards": "30",
      "translog": {
        "flush_threshold_size": "1024mb",
        "sync_interval": "120s",
        "durability": "async"
      },
      "number_of_replicas": "0",
      "merge": {
        "scheduler": {
          "max_thread_count": "1"
        }
      }
    }
  },
  "mappings": {
  },
  "aliases": {}
}

計算分片數

  • 需要注意分片數量最好設置爲節點數的整數倍,保證每一個主機的負載是差不多一樣的,特別如果是一個主機部署多個實例的情況,更要注意這一點,否則可能遇到其他主機負載正常,就某個主機負載特別高的情況。
    一般我們根據每天的數據量來計算分片,保持每個分片的大小在50G以下比較合理。如果還不能滿足要求,那麼可能需要在索引層面通過拆分更多的索引或者通過別名+按小時 創建索引的方式來實現了。

控制分片均分在各個主機上

  • 以TopicA數據的一個索引爲例,共30個分片,在10個節點上分配,應該每個節點分配3個分片,一個主機上一共有6個分片纔算是均衡。如果分配不是這樣,可以使用cerebo或者通過命令行進行分片遷移。

file
file

curl -X POST "localhost:9200/_cluster/reroute?pretty" -H 'Content-Type: application/json' -d'
{
    "commands" : [
        {
            "move" : {
                "index" : "test", "shard" : 0,
                "from_node" : "node1", "to_node" : "node2"
            }
        }
    ]
}

配置索引緩衝區

  • 即是指定 indices.memory.index_buffer_size的大小,這個是一個靜態變量,需要修改配置文件,重啓後才能生效。

參考的計算公式:indices.memory.index_buffer_size / shards_count > 512MB(超過這個值索引性能並不會有太明顯提高)
shards_count爲一個節點上面的分片數量,可以配置具體指或者一個佔用Es內存總值的百分比。這裏我們修改成了20%(默認10%)。

file

路由分片

  • 可以使用elasticsearch提供的routing特性,將數據按一定規則計算後(內部採用hash算法,把相同hash值的文檔放入同一個分片中),默認情況下是使用DocId來計算,寫入到分片,查詢時指定routing查詢,則可以提高查詢速度,避免了掃描過多的分片帶來的性能開銷。首先,在創建索引模板的時候,需要在mappings中增加配置,要求匹配到此索引模板的索引,必須配置routing:
"_routing": {      
    "required": true
  }

第二步、爲BulkPorcess創建IndexRequest時,通過routing(java.lang.String routing) 方法指定參與計算hash的值。
注意這裏是具體的值,而不是字段名稱。

經過如上的調優配置,三個Topic數據都能正常寫入,集羣文檔總數在170億,33個索引,每個索引保留4天,242個分片,整體負載不高。

file

踩過的坑

1、節點角色的設置方面

  • 如果集羣中節點數量不多,並且不需要對數據進行預處理,那麼其實可以放棄使用Ingest類型的節點。默認情況下所有的節點的默認設置都爲true。所以我們手動將主節點和數據節點做如下設置

node.ingest: false

但是需要注意一點,x-pack監控用到了這種類型的節點。會如下錯誤:
failed to flush export bulks 、no ingest node
解決辦法是,打開這個屬性配置,或者elasticsearch.yml中指定:
xpack.monitoring.exporters.my_local: type:
xpack.monitoring.exporters.local use_ingest: false

2、elasticsearch 線程池相關配置參數改變

  • 從5.0版本以後,禁止了修改各個模塊線程池的類型,線程池相關配置的前綴從threadpool 變成了thread_pool.並且線程池相關配置級別上升至節點級配置,禁止通過使用API修改,因爲場景是寫多讀少,所以我們只是增加了寫隊列的大小,配置爲: thread_pool.write.queue_size: 1000。

只能通過修改配置文件的方式修改。

3、單臺主機負載過高

  • 同一個主機兩個節點都是數據節點,並且分片分配不均勻,導致這個主機CPU使用率在98%左右,後面通過遷移分片的方式將負載降低。

4、使用自定義的routing規則後帶來的寫熱點問題

  • 比如按省份分的數據, 省份爲北京的數據過多,西藏的數據很少,可能會帶來寫熱點問題。所以合理的路由分配同樣很重要。

5、數據時區差8小時問題

  • 將業務時間轉換爲帶時區的字符串。yyyy-MM-dd'T'HH:mm:ss.SSSZ

參考文章:

http://kane-xie.github.io/201...
https://www.elastic.co/guide/...
https://elasticsearch.cn/ques...
https://juejin.im/entry/5d0f1...

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