您當前的位置:首頁 > 寵物

從 10 秒到 2 秒!ElasticSearch 效能調優

作者:由 創宇前端 發表于 寵物時間:2019-01-17

ElasticSearch效能調優

大家好,我是皮蛋二哥。

“ELK”是ElasticSearch、Logstash、Kibana三門技術的簡稱。如今ELK技術棧在網際網路行業資料開發領域使用率越來越高,做過資料收集、資料開發、資料儲存的同學相信對這個簡稱並不陌生,而ElasticSearch(以下簡稱ES)則在ELK棧中佔著舉足輕重的地位。

前一段時間,我親身參與了一個ES叢集的調優,今天把我所瞭解與用到的調優方法與大家分享,如有錯誤,請大家包涵與指正。

系統層面的調優

系統層面的調優主要是記憶體的設定與避免交換記憶體。

ES安裝後預設設定的堆記憶體是1GB,這很明顯是不夠的,那麼接下來就會有一個問題出現:我們要設定多少記憶體給ES呢?

其實這是要看我們叢集節點的記憶體大小,還取決於我們是否在伺服器節點上還是否要部署其他服務。

如果記憶體相對很大,如64G及以上,並且我們不在ES叢集上部署其他服務,那麼我建議ES記憶體可以設定為31G-32G,因為這裡有一個32G效能瓶頸問題,直白的說就是即使你給了ES叢集大於32G的記憶體,其效能也不一定會更加優良,甚至會不如設定為31G-32G時候的效能。

以我調優的叢集為例,我所調優的伺服器節點記憶體為64G,伺服器節點上也基本不跑其他服務,所以我把ES叢集記憶體大小設定為了31G,以充分發揮叢集效能。 設定ES叢集記憶體的時候,還有一點就是確保堆記憶體最小值(Xms)與最大值(Xmx)的大小是相同的,防止程式在執行時改變堆記憶體大小,這是一個很耗系統資源的過程。

還有一點就是避免交換記憶體,可以在配置檔案中對記憶體進行鎖定,以避免交換記憶體(也可以在作業系統層面進行關閉記憶體交換)。對應的引數:

bootstrap。mlockall: true

分片與副本

分片(shard):ES是一個分散式的搜尋引擎, 索引通常都會分解成不同部分, 分佈在不同節點的部分資料就是分片。ES自動管理和組織分片, 並在必要的時候對分片資料進行再平衡分配, 所以使用者基本上不用擔心分片的處理細節。建立索引時預設的分片數為5個,並且一旦建立不能更改。

副本(replica):ES預設建立一份副本,就是說在5個主分片的基礎上,每個主分片都相應的有一個副本分片。額外的副本有利有弊,有副本可以有更強的故障恢復能力,但也佔了相應副本倍數的磁碟空間。

那我們在建立索引的時候,應該建立多少個分片與副本數呢?

對於副本數,比較好確定,可以根據我們叢集節點的多少與我們的儲存空間決定,我們的叢集伺服器多,並且有足夠大多儲存空間,可以多設定副本數,一般是1-3個副本數,如果叢集伺服器相對較少並且儲存空間沒有那麼寬鬆,則可以只設定一份副本以保證容災(副本數可以動態調整)。

對於分片數,是比較難確定的。因為一個索引分片數一旦確定,就不能更改,所以我們在建立索引前,要充分的考慮到,以後我們建立的索引所儲存的資料量,否則建立了不合適的分片數,會對我們的效能造成很大的影響。 對於分片數的大小,業界一致認為分片數的多少與記憶體掛鉤,認為1GB堆記憶體對應20-25個分片,而一個分片的大小不要超過50G,這樣的配置有助於叢集的健康。但是我個人認為這樣的配置方法過於死板,我個人在調優ES叢集的過程中,根據總資料量的大小,設定了相應的分片,保證每一個分片的大小沒有超過50G(大概在40G左右),但是相比之前的分片數查詢起來,效果並不明顯。之後又嘗試了增加分片數,發現分片數增多之後,查詢速度有了明顯的提升,每一個分片的資料量控制在10G左右。 查詢大量小分片使得每個分片處理資料速度更快了,那是不是分片數越多,我們的查詢就越快,ES效能就越好呢?其實也不是,因為在查詢過程中,有一個分片合併的過程,如果分片數不斷的增加,合併的時間則會增加,而且隨著更多的任務需要按順序排隊和處理,更多的小分片不一定要比查詢較小數量的更大的分片更快。如果有多個併發查詢,則有很多小碎片也會降低查詢吞吐量。

如果現在你的場景是分片數不合適了,但是又不知道如何調整,那麼有一個好的解決方法就是按照時間建立索引,然後進行通配查詢。如果每天的資料量很大,則可以按天建立索引,如果是一個月積累起來導致資料量很大,則可以一個月建立一個索引。如果要對現有索引進行重新分片,則需要重建索引,我會在文章的最後總結重建索引的過程。

引數調優

下面我會介紹一些ES關鍵引數的調優。

有很多場景是,我們的ES叢集佔用了多大的cpu使用率,該如何調節呢。cpu使用率高,有可能是寫入導致的,也有可能是查詢導致的,那要怎麼檢視呢? 可以先透過

GET _nodes/{node}/hot_threads

檢視執行緒棧,檢視是哪個執行緒佔用cpu高,如果是

elasticsearch[{node}][search][T#10]

則是查詢導致的,如果是

elasticsearch[{node}][bulk][T#1]

則是資料寫入導致的。 我在實際調優中,cpu使用率很高,如果不是SSD,建議把

index。merge。scheduler。max_thread_count: 1

索引merge最大執行緒數設定為1個,該引數可以有效調節寫入的效能。因為在儲存介質上併發寫,由於定址的原因,寫入效能不會提升,只會降低。

還有幾個重要引數可以進行設定,各位同學可以視自己的叢集情況與資料情況而定。

index。refresh_interval

:這個引數的意思是資料寫入後幾秒可以被搜尋到,預設是1s。每次索引的refresh會產生一個新的lucene段,這會導致頻繁的合併行為,如果業務需求對實時性要求沒那麼高,可以將此引數調大,實際調優告訴我,該引數確實很給力,cpu使用率直線下降。

indices。memory。index_buffer_size

:如果我們要進行非常重的高併發寫入操作,那麼最好將

indices。memory。index_buffer_size

調大一些,index buffer的大小是所有的shard公用的,一般建議(看的大牛部落格),對於每個shard來說,最多給512mb,因為再大效能就沒什麼提升了。ES會將這個設定作為每個shard共享的index buffer,那些特別活躍的shard會更多的使用這個buffer。預設這個引數的值是10%,也就是jvm heap的10%。

translog

:ES為了保證資料不丟失,每次index、bulk、delete、update完成的時候,一定會觸發重新整理translog到磁碟上。在提高資料安全性的同時當然也降低了一點效能。如果你不在意這點可能性,還是希望效能優先,可以設定如下引數:

“index。translog”

{

“sync_interval”

“120s”

——sync間隔調高

“durability”

“async”

-–

非同步更新

“flush_threshold_size”

“1g”

——log檔案大小

}

這樣設定的意思是開啟非同步寫入磁碟,並設定寫入的時間間隔與大小,有助於寫入效能的提升。

還有一些超時引數的設定:

discovery。zen。ping_timeout 判斷master選舉過程中,發現其他node存活的超時設定

discovery。zen。fd。ping_interval 節點被ping的頻率,檢測節點是否存活

discovery。zen。fd。ping_timeout 節點存活響應的時間,預設為30s,如果網路可能存在隱患,可以適當調大

discovery。zen。fd。ping_retries ping失敗/超時多少導致節點被視為失敗,預設為3

其他建議

還有一些零碎的最佳化建議喔。

插入索引自動生成id:當寫入端使用特定的id將資料寫入ES時,ES會檢查對應的索引下是否存在相同的id,這個操作會隨著文件數量的增加使消耗越來越大,所以如果業務上沒有硬性需求建議使用ES自動生成的id,加快寫入速率。

避免稀疏索引:索引稀疏之後,會導致索引檔案增大。ES的keyword,陣列型別採用doc_values結構,即使欄位是空值,每個文件也會佔用一定的空間,所以稀疏索引會造成磁碟增大,導致查詢和寫入效率降低。

我的調優

下面說一說我的調優:我的調優主要是重建索引,更改了現有索引的分片數量,經過不斷的測試,找到了一個最佳的分片數量,重建索引的時間是漫長的,在此期間,又對ES的寫入進行了相應的調優,使cpu使用率降低下來。附上我的調優引數。

index。merge。scheduler。max_thread_count:1 #索引merge最大執行緒數

indices。memory。index_buffer_size:30% #記憶體

index。translog。durability:async #這個可以非同步寫硬碟,增大寫的速度

index。translog。sync_interval:120s #translog間隔時間

discovery。zen。ping_timeout:120s #心跳超時時間

discovery。zen。fd。ping_interval:120s #節點檢測時間

discovery。zen。fd。ping_timeout:120s #ping超時時間

discovery。zen。fd。ping_retries:6 #心跳重試次數

thread_pool。bulk。size:20 #寫入執行緒個數 由於我們查詢執行緒都是在程式碼裡設定好的,我這裡只調節了寫入的執行緒數

thread_pool。bulk。queue_size:1000 #寫入執行緒佇列大小

index。refresh_interval:300s #index重新整理間隔

關於重建索引

在重建索引之前,首先要考慮一下重建索引的必要性,因為重建索引是非常耗時的。 ES的reindex api不會去嘗試設定目標索引,不會複製源索引的設定,所以我們應該在執行_reindex操作之前設定目標索引,包括設定對映(mapping),分片,副本等。

第一步,和建立普通索引一樣建立新索引。當資料量很大的時候,需要設定重新整理時間間隔,把

refresh_intervals

設定為-1,即不重新整理,

number_of_replicas

副本數設定為0(因為副本數可以動態調整,這樣有助於提升速度)。

{

“settings”

{

“number_of_shards”

“50”

“number_of_replicas”

“0”

“index”

{

“refresh_interval”

“-1”

}

}

“mappings”

{

}

}

第二步,呼叫reindex介面,建議加上

wait_for_completion=false

的引數條件,這樣reindex將直接返回taskId。

POST

_reindex?wait_for_completion=

false

{

“source”

{

“index”

“old_index”

//原有索引

“size”

5000

//一個批次處理的資料量

},

“dest”

{

“index”

“new_index”

//目標索引

}

}

第三步,等待。可以透過

GET _tasks?detailed=true&actions=*reindex

來查詢重建的進度。如果要取消task則呼叫

_tasks/node_id:task_id/_cancel

第四步,刪除舊索引,釋放磁碟空間。更多細節可以檢視ES官網的reindex api。

那麼有的同學可能會問,如果我此刻ES是實時寫入的,那咋辦呀? 這個時候,我們就要重建索引的時候,在引數里加上上一次重建索引的時間戳,直白的說就是,比如我們的資料是100G,這時候我們重建索引了,但是這個100G在增加,那麼我們重建索引的時候,需要記錄好重建索引的時間戳,記錄時間戳的目的是下一次重建索引跑任務的時候不用全部重建,只需要在此時間戳之後的重建就可以,如此迭代,直到新老索引資料量基本一致,把資料流向切換到新索引的名字。

POST

/_reindex

{

“conflicts”

“proceed”

//意思是衝突以舊索引為準,直接跳過沖突,否則會丟擲異常,停止task

“source”

{

“index”

“old_index”

//舊索引

“query”

{

“constant_score”

{

“filter”

{

“range”

{

“data_update_time”

{

“gte”

123456789

//reindex開始時刻前的毫秒時間戳

}

}

}

}

}

},

“dest”

{

“index”

“new_index”

//新索引

“version_type”

“external”

//以舊索引的資料為準

}

}

以上就是我在ES調優上的一點總結,希望能夠幫助到對ES效能有困惑的同學們,謝謝大家。 ——我是皮蛋,我喂自己袋鹽。

文 / 皮蛋二哥

“一直以為只要保持低調,就沒人知道其實我是一名作家”

編 / 熒聲

作者往期文章:

本文由創宇前端作者授權釋出,版權屬於作者,創宇前端出品。 歡迎註明出處轉載本文。文章連結:https://knownsec-fed。com/2019。。。

想要訂閱更多來自知道創宇開發一線的分享,請搜尋關注我們的微信公眾號:創宇前端(KnownsecFED)。歡迎留言討論,我們會盡可能回覆。

歡迎點贊、收藏、留言評論、轉發分享和打賞支援我們。打賞將被完全轉交給文章作者。

感謝您的閱讀。

標簽: 索引  分片  ES  index  調優