背景
筆者維護的線上EFK集羣,在每天早上8點創建新索引的時候,日誌中總會出現如下的日誌:
failed to process cluster event (cluster_update_settings) within 30s
[2019-04-13T08:00:38,213][DEBUG][o.e.a.b.TransportShardBulkAction] [logstash-felice-query-2019.04.13][2] failed to execute bulk item (index) BulkShardRequest [[logstash-felice-query-2019.04.13][2]] containing [7] requests
org.elasticsearch.cluster.metadata.ProcessClusterEventTimeoutException: failed to process cluster event (put-mapping) within 30s
at org.elasticsearch.cluster.service.MasterService$Batcher.lambda$onTimeout$0(MasterService.java:124) ~[elasticsearch-6.3.0.jar:6.3.0]
at java.util.ArrayList.forEach(ArrayList.java:1257) ~[?:1.8.0_152]
at org.elasticsearch.cluster.service.MasterService$Batcher.lambda$onTimeout$1(MasterService.java:123) ~[elasticsearch-6.3.0.jar:6.3.0]
at org.elasticsearch.common.util.concurrent.ThreadContext$ContextPreservingRunnable.run(ThreadContext.java:625) ~[elasticsearch-6.3.0.jar:6.3.0]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_152]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_152]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_152]
問題原因
簡言之:es的master處理不過來了,es的segment合併是一個非常耗時的操作。
批量處理的超時時間默認設置爲30s。
可以通過以下命令查看pending的task:
curl 'localhost:9200/_cat/pending_tasks?v'
網上說法不一,出現這個問題很可能的原因可以總結如下:
- 分片數過多;單節點的分片數不要超過1000個(經驗值);
- 通過寫入數據自動創建索引最容易出現這種情況;
- 大批量寫入數據refresh時間間隔太短;
- 索引的字段數量太多(幾十上百個)
解決方案
方案一:每天預創建索引
雖然普通的EFK日誌服務可以通過logstash的默認模板去創建索引,但是當索引個數比較大,並且索引的字段數量太多時,就非常容易出現超時。那麼我們一般的做法是提前創建好索引,並且設定好每個索引的mapping。
筆者採用了一種偷懶的操作,寫了一個定時任務,每天去讀取當天的索引的mapping,然後直接用這個mapping去創建新的索引。當然這種解決方案的前提是你自己已經對索引有了一個基本的管理。
方案二:減少每個節點的分片數
es默認爲每個索引設置的分片數是5。可以通過以下命令查看索引的settings:
GET /logstash-sonofelice-2019.04.27
可以在創建索引的時候,優化一下分片的個數。可以參考文章:https://www.cnblogs.com/gugul...
一句話:shard越少越好。shard越少,越能減少寫入開銷。
方案三:集羣擴容
如果是偶發性的寫入超時,可能是流量高峯或者新創建索引的時候創建mapping新字段導致。
但是如果經常性的出現超時,並且write_queue裏面的數據太多,cpu使用率居高不下,那可能就是es集羣出現了瓶頸,這時候就需要進行擴容,沒有更好的辦法。