在ES的默認設置下,是綜合考慮數據的可靠性,搜索實時性,寫入速度等因素的。當離開默認設置,追求極致寫入速度時,很多是以犧牲可靠性和搜索實時性爲代價的。有時候,業務上對數據可靠性和搜索實時性要求不高,反而對寫入速度要求很高,此時可以調整一些策略,最大化寫入速度。
綜合來說可以從以下幾個方面入手:
- 加大translog flush間隔,目的是降低iops,writeblock (可靠性降低)
- 加大index refresh間隔,除了降低I/O,更重要的是降低segment merge頻率
- 調整bulk請求(批處理)
- 優化磁盤間的任務均勻情況,將shard儘量均勻分佈到物理機各個磁盤
- 優化節點間的任務分佈,將任務儘量均勻地發到各節點
- 優化Lucene層建立索引的過程,目的是降低CPU佔用率,例如,禁用_all字段
translog flush
ES請求進行,先會寫入到translog文件中,在ES 2.x開始,默認情況下,translog的持久化策略爲:每個請求都“flush”。對應配置
index.translog.durability:request
這會影響ES寫入的最大因素。但是隻有這樣,寫操作纔可能是最可靠的,如果系統允許接收一定概率的數據丟掉,則可以調整translog持久化策略爲週期性和一定大小的時候“flush”。
設置translog策略爲異步,時間120s
index.translog.durability:async
設置刷盤時間爲120s,默認5s
index.translog.sync_interval:120s
超過設置大小會導致refresh操作,產生新的Lucene分段。默認爲512MB
index.translog.flush_threshold_size:1024mb
索引刷新間隔refresh_interval
默認情況下索引的refresh_interval爲1秒,這意味着數據寫如1秒後就可以被搜索到,每次索引的refresh會產生一個新的Lucene段(segment),試想以下,如果segment過多會怎麼樣,因此ES 會進行segment merge 段合併,如果不需要這麼高的搜索實時性,可以適當降低refresh週期,如下:
index.refresh_interval:120s
segment段合併優化
segment merge 操作對系統I/O和內存佔用都比較高,從ES 2.0 開始,merge操作不再由ES 控制,而是由Lucene 控制,改爲以下
index.merge.scheduler.max_thread_count
index.merge.policy.*
最大線程數max_thread_count默認值是:
Math.max(1,Math.min(4,Runtime.getRuntime().availableProcessors()/2))
這是一個比較理想的值,如果只是一塊硬盤而非SSD,則應該設置爲1,因爲在旋轉存儲介質上併發寫,由於尋址原因,只會降低寫入速度。
merge策略index.merge.policy有三種
- tiered(默認)
- log_byete_size
- log_doc
索引創建時合併策略就已確定,不能進行修改,但是可以動態更新策略參數,可以不做此項調整。
如果堆棧經常有很多merge,則可以嘗試調整以下策略配置:
index.merge.policy.segments_per_tier
該屬性指定了每層分段的數量,取值越小最終segment越少,因此需要merge操作越多,可以考慮適當增加值,默認10,其應該大於等於index.merge_at_once
index.merge.policy.max_merged_segment
指定單個segment最大容量,默認5GB,可以適當降低
index buffer
indexing buffer 在爲doc建立索引時使用,當緩衝滿時會刷入磁盤,生成一個新的segment,這是除了refresh_interval 刷新索引外,另一個生成新segment的機會。每個shard有自己的indexing buffer,下面的這個buffer大小的配置需要除以這個節點上索引shard的數量:
indices.memory.index_buffer_size
默認是整個堆空間的10%
indices.memory.min_index_buffer_size
默認48MB
indices.memory.max_index_buffer_size
默認無限制
在執行大量的索引操作時,indices.memory.index_buffer_size的默認設置可能不夠,這和可用堆內存,單節點上的shard數量相關,可以考慮適當增大該值。
使用bulk請求
批量寫比一個索引請求只寫單個文檔的效率高得多,但是要注意bulk請求得整體字節數不要太大,太大可能給集羣帶來內存壓力,因此每個請求最好避免超過幾十MB,即使較大得請求看上去執行可能更好。
索引建立過程屬於CPU密集型任務,應該使用固定大小的線程池,來不及處理的任務放入隊列。這樣可以減少上下文的切換帶來的性能消耗,隊列大小要適當,過大的隊列導致較高的GC壓力,並可能導致FGC頻繁發生。
bulk寫請求是一個長任務,爲了給系統增加足夠的寫入 壓力,寫入過程應該多個客戶端,多個線程冰箱執行。
磁盤間的任務均衡
ES 在分配shard的時候,落到各個磁盤的shard可能並不均勻,這種不均勻可能導致某些磁盤繁忙,對寫入性能會產生一定的影響
節點間的任務均衡,爲了節點間的任務儘量均衡,數據寫入客戶端應該把bulk請求輪詢發送到各個節點。
索引過程調整和優化
1.自動生成docID(避免ES對自定義ID驗證的操作)
2.調整字段Mapping
- 減少不必要的字段數量
- 將不需要建立索引字段的index屬性設置爲not_analyzed或no。對字段不分詞或不建立索引,減少相應的操作,特別是binary類型
- 減少字段內容長度
- 使用不同的分析器(analyzer),不同分析器之間的運算複雜度也不相同
3.調整_source字段
_source字段用於存儲doc原始數據,對於部分不需要存儲的字段,可以通過includes excludes過濾,或者禁用_source,一般實際場景不會禁用
4.禁用_all
從ES 6.0開始,_all字段默認不啓用,_all字段中包含所有字段分詞後的關鍵詞,作用是可以搜索的時候不指定特定字段,從所有字段所有中減少。
5.對Analyzed的字段禁用Norms
Norms用於在搜索時計算doc的評分,如果不需要評分,則可以將其禁用:
"title":{"type":"string","norms":{"enabled":false}}
6.index_options設置
index_options用於控制在建立倒排索引過程中,哪些內容會被添加到倒排索引中,例如,doc數量,詞頻,positions,offsets等信息,優化這些設置可以一定程度上降低索引過程中的計算任務,接收CPU佔用率(注:實際場景一般不會用,除非方案一開始很明確)
參考配置