【Elasticsearch】優秀實踐-Elasticsearch寫入調優

基本優化手段

Elasticsearch默認的設置和參數配置下,是綜合考慮了數據的可靠性、搜索實時性、寫入速度等因素。但在某些場景中,業務對數據的可靠性和搜索實時性要求並不高,反而對寫入速度要求高,可以通過調整一些策略,優化寫入速度。

綜合來說,提升寫入速度可以從以下幾個方面入手:

1) 加大index refresh間隔,除了降低IO,更重要的是降低了segment merge的頻率;
2) 加大translog refresh間隔,目的是降低iops、writeblock;
3) 調整bulk請求,控制每次bulk請求提交的量;
4) 優化磁盤間的任務均勻情況,將shard儘量均勻分佈到機器的各個磁盤;
5) 優化節點間的任務分佈,將任務儘量均勻地發佈到各節點,避免單點阻塞問題;
6) 優化Lucene層建立索引的過程,目的是降低CPU佔用率及IO,例如,禁用_all字段。

索引刷新間隔

默認情況下索引的refresh_interval爲1秒,這意味着數據寫1秒後就可以被搜索到,每次索引的refresh會產生一個新的lucene段,這會導致頻繁的segment merge行爲,如果你不需要這麼高的搜索實時性,應該降低索引refresh週期,如:

index.refresh_interval: 120s

Translog刷新間隔

默認設置下,translog的持久化策略爲:每個請求都flush。對應配置項爲:

index.translog.durability: request

這是影響Elasticsearch寫入速度的最大因素。但是隻有這樣,寫操作纔有可能是可靠的,如果系統可以接受一定機率的數據丟失,調整translog持久化策略爲週期性和一定大小的時候flush:

index.translog.durability: async index.translog.sync_interval: 120s
index.translog.flush_threshold_size: 3gb
index.translog.flush_threshold_period: 120m

段合併優化

segment merge操作對系統CPU和IO佔用都比較高,從es 2.0開始,merge行爲不再由es控制,而是轉由lucene控制。有以下調整開關:

index.merge.scheduler.max_thread_count:最大線程數
index.merge.policy.*

最大線程數的默認值爲:

Math.max(1, Math.min(4, Runtime.getRuntime().availableProcessors() / 2))

是一個比較理想的值,如果你只有一塊硬盤並且非SSD,應該把他設置爲1,因爲在旋轉存儲介質上併發寫,由於尋址的原因,不會提升,只會降低寫入速度。

index.merge.policy.floor_segment

該屬性用於阻止segment的頻繁flush,小於此值將考慮優先合併,默認爲2M,可考慮適當降低此值,如1M。

index.merge.policy.segments_per_tier

該屬性指定了每層分段的數量,取值越小最終segment越少,因此需要merge的操作更多,可以考慮適當增加此值。默認爲10,他應該大於等於 index.merge.policy.max_merge_at_once

“merge”:{
“scheduler”:{
“max_thread_count” : “1”
},
“policy”:{
“segments_per_tier” : “20”,
“max_merge_at_once”: “20”,
“floor_segment” : “1m”,
“max_merged_segment” : “5g”
}
}

Indexing Buffer

indexing buffer在爲doc建立索引時使用,當緩衝滿時會刷入磁盤,生成一個新的 segment,這是除refresh_interval外另外一個刷新索引,生成新segment的機會。每個 shard有自己的indexing buffer,下面的關於這個buffer大小的配置需要除以這個節點上所有的shard數量。

indices.memory.index_buffer_size

默認爲整個堆的10%,如30GB堆內存,約佔300M,在大量的索引操作時,可以考慮適當增大,但不要超過20%。

Bulk請求

1) 使用批量請求,設置合理的數據條數:使用bulk命令進行批量索引數據時,每批次提交的數據大小爲5~15MB;比如每條數據大小爲1k,那麼建議批量提交的數據條數爲5000條;當前集羣的最佳批量請求大小,可以從5MB開始測試,緩慢增加這個大小,直到寫入性能不能提升爲止。

2) 每個批量請求中只處理一個索引的數據:一個bulk請求只寫入一個索引的數據,不建議一個bulk請求同時寫入多個索引的數據,不同索引的數據分多個bulk請求提交。

3) 建立索引的過程偏計算密集型任務,應該使用固定大小的線程池配置,來不及處理的放入隊列,線程數量配置爲CPU核心數+1,避免過多的上下文切換。隊列大小可以適當增加,但一定要嚴格控制大小,過大的隊列導致較高的GC壓力,並可能導致FGC頻繁發生。

磁盤間的任務均衡

如果你的部署方案是爲path.data配置多個路徑來使用多塊磁盤, es在分配shard 時,落到各磁盤上的shard可能並不均勻,這種不均勻可能會導致某些磁盤繁忙,利用率達到100%,這種不均勻達到一定程度可能會對寫入性能產生負面影響。

所以建議每個數據實例掛載單塊磁盤。

節點間的任務均衡

爲了在節點間任務儘量均衡,數據寫入客戶端應該把bulk請求輪詢發送到各個節點。
當使用java api ,或者rest api的bulk接口發送數據時,客戶端將會輪詢的發送的集羣節點,節點列表取決於:
當client.transport.sniff爲true,(默認爲 false),列表爲所有數據節點。
否則,列表爲初始化客戶端對象時添加進去的節點。
java api的TransportClient和rest api的RestClient都是線程安全的,當寫入程序自己創建線程池控制併發,應該使用同一個Client對象。在此建議使用rest api,兼容性好,只有吞吐量非常大才值得考慮序列化的開銷,顯然搜索並不是高吞吐量的業務。
觀察bulk請求在不同節點上的處理情況,通過cat接口觀察bulk線程池和隊列情況,是否存在不均:

_cat/thread_pool/bulk?v

另外,也可以搭建NGINX服務來達到負載均衡。

分片均勻分佈

配置total_shards_per_node參數,讓分片更加均勻的分佈在各個實例上。表示限制每個實例上分佈該該索引的分片最大個數,如2,即表示每個實例上最多分佈2個該索引的分片。

說明:total_shards_per_node參數值=索引總分片數/數據實例數(向上取整)。

索引過程調整和優化

自動生成doc ID

分析es寫入流程可以看到,寫入doc時如果是外部指定了id,es會先嚐試讀取原來doc的版本號,判斷是否需要更新,使用自動生成doc id可以避免這個環節,從而避免出現在磁盤IOPS能力不足的情況下,磁盤IO被讀IO大量佔用,導致寫入速度受限於磁盤IO。

調整字段 Mappings

1) 字段的index屬性設置爲: not_analyzed,或者no。對字段不分詞,或者不索引,可以節省很多運算,降低CPU佔用。尤其是binary類型,默認情況下佔用CPU非常高,而這種類型根本不需要進行分詞做索引。
2) 調整_source字段:_source字段用於存儲doc原始數據,對於部分不需要存儲的字段,可以通過includes excludes來過濾,或者將_source 禁用,一般用於索引和數據分離。
3) 禁用_all字段:_all字段默認是開啓的,其中包含所有字段分詞後的關鍵詞,作用是可以在搜索的時候不指定特定字段,從所有字段中檢索。如果你不需要這個特性,可以禁用_all,可以小幅的降低CPU 壓力,對速度影響並不明顯。

小結

over

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