目錄
ES數據寫入過程:
數據寫入請求——>
協調節點接收后數據路由處理——>
存入對應數據節點的 index buffer 並記錄 translog 日志——>
經過 refresh 刷新為 segment 存入文件緩存並變為可搜索——>
數據永久刷新到磁盤並清空 translog 日志
在了解了數據寫入過程后,我們可以針對寫入過程進行優化。
一:分片延遲分配
說明:
ES會自動在可用節點間進行分片均衡,包括新節點的加入和現有節點離線都會觸發這個動作。
例:
- 在集群中,有一個node因為故障導致意外關機重啟
- Master立即注意到這個節點重啟,他會把集群內其他節點擁有掉線節點主節點對應的副本升級為主節點
- 在副本被提升為主節點后,master節點開支執行重建確實副本的操作,集群內的節點間互相拷貝數據,導致網卡,IO性能增加
- 由於集群現在處於非平衡狀態,這個過程可能會觸發小規模的分片移動。其他不相關的分片將會在節點間遷移來達到一個最佳的平衡狀態
- 剛剛掉線的節點重啟好了,重新加入集群,被告知,當前的數據已經失效了,節點會把當前數據刪除,然后集群又開始重新分配分配,引起集群內資源的消耗
- 在日常工作中,主機和服務的重啟都無法避免,如果重啟時間比較短的情況下,如果發生上述的過程,會消耗過多的資源,這是我們不希望看到的。
操作:
- 默認情況下,集群會等待一分鍾來查看節點是否會重新加入集群,如果集群在這個時間內重新加入集群,重新加入的節點會保持現有的分配數據,不會觸發新的分片分配。
- 通過delayed_timeout,默認等待時間可以全局設置,也可以在索引級別進行設置:
PUT /_all/_settings { "settings": { "index.unassigned.node_left.delayed_timeout": "5m" } }
- _all: 所有索引,也可以修改為單個索引名。
- 5m: 默認時間修改為5min,也可以設置為
0m
,立即分配
NOTE
延遲分配不會阻止副本被提拔為主分片。集群還是會進行必要的提拔來讓集群回到 yellow 狀態。缺失副本的重建是唯一被延遲的過程。
自動取消分片遷移
- 如果節點在超時之后回來,集群還沒有完成分片的遷移操作,這個時候,Elasticsearch會檢查上線的節點的數據是否和當前活躍主分片的數據是否一直,如果兩者匹配,說明沒有新的文檔存儲進來,這個時候,集群會取消分片的操作,並啟用改節點上的數據,因為磁盤比網絡更快,這樣更合乎常理。
- 但是如果已經產生分歧,在節點離線這段時間內,離線節點的主索引的數據不一致,這個時候還是會按照正常流程來進行操作。
二:批量請求
說明:
在進行寫操作時,要確保批量請求是輪詢的發往你的所有數據節點,不要把所有的請求都發給單個節點,因為這個節點會需要在處理的時候把所有的批量請求都存在內存里。
三:存儲
- 使用讀寫性能比較好的磁盤,比如SSD
- 使用RAID 0,帶區卷會提高磁盤I/O性能,不要使用鏡像卷和校驗卷,應為副本已經提供了冗余的功能
- 使用多塊磁盤,並允許Elasticsearch通過path.data目錄配置把數據分配到其中
- 不要使用遠程掛載的存儲,比如NFS或者SMB/CIFS,引入網絡延遲對性能來說完全是背道而馳。
四:段合並
段合並的計算量龐大,而且還要吃掉大量磁盤I/O。合並在后台定期操作,因為他們可能要很長時間才能完成,尤其是比較大的段。這個通常來說都沒問題,因為大規模合並段的概率是很小的。
- 如果發生合並影響寫入速度,Elasticsearch會自動限制索引請求到單個線程,這個可以防止段爆炸的問題,即數以百計的段在合並之前就生成出來。如果Elasticsearch發現合並拖累索引了,它會記錄一個申明有now throttling indexing的INFO級別信息。
- elasticsearch的默認設置比較保守,默認值為20MB/s,對機械磁盤這個值還不錯,如果使用的SSD磁盤,這個值應該在100-200MB/s。
PUT /_cluster/settings { "persistent" : { "indices.store.throttle.max_bytes_per_sec" : "100mb" } }
- 如果我們在做數據的批量導入操作,我們可以徹底關閉合並限流。這可以讓你的索引跑到磁盤性能的極致。
當數據導入完成后,需要改過merge重新打開限流。PUT /_cluster/settings { "transient" : { "indices.store.throttle.type" : "none" } }
- 可以增加index.translog.flush_threshold_size的值,默認為512,這可以清空觸發事物日志里累計出更大的段,從而構建出更大的段,清空的頻率變低,大段合並的頻率也變低。這會導致更少的磁盤I/O和索引速率,但是你需要更大的heap內存用來存儲更大的緩沖空間。
- 對於refresh而言,每一次的操作,都會產生一個段,而后台又會自動將小段合成一個比較大的段,但是對於ES來說,段還是比較小的,默認來說,每台主機,上的每個shard,會被合並成17個段左右,每個段都會消耗文件句柄,內存和cpu運行周期,更重要的是,每個搜索請求都必須輪流檢查每個段;所以段越多,搜索也就越慢。
- 段的合並默認是系統自己進行的,但是我們也可以強制合成更多的段;強制合並應該只針對只讀索引,在寫索引上執行下列操作,會產生超過5GB的索引,並且合並策略不會再考慮對它進行合並,會導致非常大的段留在碎片中。官網資料
# 強制每個shard合並成一個段 POST web-2018.06.08/_forcemerge?max_num_segments=1
五:索引刷新頻率
index level:調整刷新頻率
- 如果我們不需要接近實時的獲取數據,我們可以把索引的頻率調長一點,修改“index.refresh_interval”參數為30s。
- 如果在做大批量的導入,我們可以關閉刷新,將參數的值修改為“-1”,當然在最后別忘了開啟。
node level:調整索引緩沖區
- 索引緩沖區用於存儲新索引的文檔,存於內存中,索引緩沖區的默認大小為10%,當他的空間使用滿了以后,會將索引從內存中寫入到磁盤緩沖區,生產segment,並變為可搜索狀態。
- 如果我們需要修改這個配置,必須在cluster中的每個data節點進行修改,這是一個靜態配置:
indices.memory.index_buffer_size 接受百分比或字節大小值,默認為10%,意味着分配給node的總內存的10%用於索引緩沖區 indices.memory.min_index_buffer_size 如果將index_buffer_size設置為備份比,則可以用此設置指定絕對最小值,默認為48mb indices.memory.max_index_buffer_size 如果將index_buffer_size設置為百分比,則可以用此設置指定絕對最小值,默認無限制
六:關閉副本
- 文檔在復制的時候,整個文檔內容都被發往副本節點,然后逐字的把索引過程復制一遍,這也意味着副本也會執行分析,索引以及可能的合並過程。
- 如果索引是零副本,在寫入后再開啟副本,恢復過程本質上只是復制的操作。
七:友好的ID
- 如果你沒有給文檔指定id,那么Elasticsearch會自動生成ID
- 如果要使用自己的ID,想UUID-4這樣的ID,本質上是隨機的,壓縮比很低,會明顯拖慢lucene,這個使用我們就需要用lucene友好的ID。
八:日志記錄
日志等級
- Elasticsearch默認的日志等級為
INFO
,他提供了適度的信息,但是又不至於讓日志過於龐大。 - 但是當我們排錯時,
INFO
的日志信息局限性又比較大,這個時候就需要調整日志級別到DEBUG
,我們可以通過logging.yml文件設置,但是這樣需要重啟。 - 我們也可以通過API動態調整。
PUT /_cluster/settings { "transient" : { "logger.discovery" : "DEBUG" } }
慢日志
- 慢日志默認是不開啟的,如果需要開啟,要定義動作(query,fetch和index),你期望的記錄等級,還有時間閾值。
PUT /my_index/_settings { "index.search.slowlog.threshold.query.warn" : "10s", "index.search.slowlog.threshold.fetch.debug": "500ms", "index.indexing.slowlog.threshold.index.info": "5s" }
- 查詢慢於10s的輸出一個WARN日志
- 獲取慢於500ms的輸出一個DEBUG日志
- 索引慢於5s的輸出一個INFO日志
- 這個日志也可以在logging.yml文件中設置
- 一但閾值設置過了,就可以像其他日志一樣切換日志等級:
PUT /_cluster/settings { "transient" : { "logger.index.search.slowlog" : "DEBUG", "logger.index.indexing.slowlog" : "WARN" } }
- 設置搜索慢日志為DEBUG級別
- 設置索引慢日志為WARN級別
九:節點下線
十:使用 multiple workers/threads發送數據到ES
多進程或者線程
如果看到TOO_MANY_REQUESTS (429)和EsRejectedExecutionException則說明ES跟不上索引的速度,當集群的I/O或者CPU飽和就得到了工作者的數量。
十一:減少索引並發訪問磁盤
如果是機械硬盤,你需要增加下面的配置到elasticsearch.yml中
index.merge.scheduler.max_thread_count: 1
機械硬盤的並發IO性能較差,我們需要減少每個索引並發訪問磁盤的線程數,這個設置會有max_thread_count+2個線程並發訪問磁盤
如果是SSD可以忽略這個參數,默認線程數是Math.min(3, Runtime.getRuntime().availableProcessors() / 2),對於SSD來說沒有問題。
十二:Rollover 自動拆分索引
當滿足指定的條件后,索引就會被自動拆分
指定方法:
PUT /logs-000001
{
"aliases": {
"logs_write": {}
}
}
# Add > 1000 documents to logs-000001
POST /logs_write/_rollover
{
"conditions": {
"max_age": "7d", # 最大的時間限制
"max_docs": 1000 # 最大的條數
}
}
注意事項:
- 索引命名規則必須如同:logs-000001。
- 索引必須指定 aliases。
- Rollover Index API 調用時才去檢查索引是否超出指定規則,不會自動觸發,需要手動調用,可以通過 Curator 實現自動化。
- 如果符合條件會創建新的索引,老索引的數據不會發生變化,如果你已經插入 2000 條,拆分后還是 2000 條。
- 插入數據時一定要用別名,否則你可能一直在往一個索引里追加數據。
示例:
假如生成的索引名為 logs-2017.04.13-1,如果你在當天執行 Rollover 會生成 logs-2017.04.13-000001,次日的話是 logs-2017.04.14-000001。