批量送出
當有大量資料送出的時候,建議采用批量送出。比如在做 ELK 過程中 ,Logstash indexer 送出資料到 Elasticsearch 中 ,batch size 就可以作為一個優化功能點。但是優化 size 大小需要根據文檔大小和伺服器性能而定。像 Logstash 中送出文檔大小超過 20MB ,Logstash 會請一個批量請求切分為多個批量請求。如果在送出過程中,遇到 EsRejectedExecutionException 異常的話,則說明叢集的索引性能已經達到極限了。這種情況,要麼提高伺服器叢集的資源,要麼根據業務規則,減少資料收集速度,比如隻收集 Warn、Error 級别以上的日志。
優化硬體
在經濟壓力能承受的範圍下, 盡量使用固态硬碟 SSD。SSD 相對于機器硬碟,無論随機寫還是順序寫,都較大的提升。
磁盤備份采用 RAID0。因為 Elasticsearch 在自身層面通過副本,已經提供了備份的功能,是以不需要利用磁盤的備份功能,同時如果使用磁盤備份功能的話,對寫入速度有較大的影響。
增加Refresh間隔
為了提高索引性能,Elasticsearch 在寫入資料時候,采用延遲寫入的政策,即資料先寫到記憶體中,當超過預設 1 秒 (index.refresh_interval)會進行一次寫入操作,就是将記憶體中 segment 資料重新整理到作業系統中,此時我們才能将資料搜尋出來,是以這就是為什麼 Elasticsearch 提供的是近實時搜尋功能,而不是實時搜尋功能。
内部系統對資料延遲要求不高的話,我們可以通過延長 refresh 時間間隔,可以有效的減少 segment 合并壓力,提供索引速度。在做全鍊路跟蹤的過程中,我們就将 index.refresh_interval 設定為 30s,減少 refresh 次數。同時,在進行全量索引時,可以将 refresh 次數臨時關閉,即 index.refresh_interval 設定為 -1,資料導入成功後再打開到正常模式,比如 30s。
減少副本數量
Elasticsearch 預設副本數量為 3 個,雖然這樣會提高叢集的可用性,增加搜尋的并發數,但是同時也會影響寫入索引的效率。
在索引過程中,需要把更新的文檔發到副本節點上,等副本節點生效後在進行傳回結束。副本數量多的時候可以提高叢集的可用性,增加搜尋的并發數,但是同時也會影響寫入索引的效率,使用 Elasticsearch 做業務搜尋的時候,副本建立盡量結合業務特性,但是内部 ELK 日志系統、分布式跟蹤系統中,完全可以将副本數目設定為 1 個。
查詢性能優化:
路由:
當我們查詢文檔的時候,Elasticsearch 如何知道一個文檔應該存放到哪個分片中呢 ?
它其實是通過下面這個公式來計算出來
shard = hash(routing) % number_of_primary_shards(routing 預設值是文檔的 id,也可以采用自定義值,比如使用者 id。)
不帶 routing 查詢:
在查詢的時候因為不知道要查詢的資料具體在哪個分片上,是以整個過程分為 2 個步驟
分發:請求到達協調節點後,協調節點将查詢請求分發到每個分片上。
聚合: 協調節點搜集到每個分片上查詢結果,在将查詢的結果進行排序,之後給使用者傳回結果。
帶 routing 查詢:
查詢的時候,可以直接根據 routing 資訊定位到某個配置設定查詢,不需要查詢所有的配置設定,經過協調節點排序。向上面自定義的使用者查詢,如果 routing 設定為 userid 的話,就可以直接查詢出資料來,效率提升很多
Filter VS Query:
盡可能使用過濾器上下文(Filter)替代查詢上下文(Query),Elasticsearch 針對 Filter 查詢隻需要回答「是」或者「否」,不需要像 Query 查詢一下計算相關性分數,同時 Filter 結果可以緩存。
Query:此文檔與此查詢子句的比對程度如何?
Filter:此文檔和查詢子句比對嗎?
大翻頁:
在使用 Elasticsearch 過程中,應盡量避免大翻頁的出現。正常翻頁查詢都是從 From 開始 Size 條資料,這樣就需要在每個分片中查詢打分排名在前面的 From + Size 條資料。協同節點收集每個配置設定的前 From + Size 條資料。協同節點一共會受到 N * ( From + Size )條資料,然後進行排序,再将其中 From 到 From + Size 條資料傳回出去。
如果 From 或者 Size 很大的話,導緻參加排序的數量會同步擴大很多,最終會導緻 CPU 資源消耗增大。可以通過使用 Elasticsearch scroll 和 scroll-scan 高效滾動的方式來解決這樣的問題
JVM 設定
标準的建議是把 50% 的可用記憶體作為 Elasticsearch 的堆記憶體,保留剩下的 50%。當然它也不會被浪費,Lucene 會很樂意利用起餘下的記憶體,并且最大記憶體設定不要超過32GB(具體原因可以參考 連接配接)
JVM 還有一個配置 bootstrap.mlockall: true,比較重要。這是讓 JVM 啟動的時候就 鎖定 heap 記憶體。
其他優化項:
hreadpool.search.queue_size 這個配置是很重要的(線程池相關),一般預設是夠用了,可以嘗試提高
max_num_segments 建議配置為1。雖然優化時間會變長,但是在高峰期前能完成的話,會對查詢性能有很大好處
elasticsearch-段合并
由于自動重新整理流程每秒會建立一個新的段 ,這樣會導緻短時間内的段數量暴增。而段數目太多會帶來較大的麻煩。 每一個段都會消耗檔案句柄、記憶體和cpu運作周期。更重要的是,每個搜尋請求都必須輪流檢查每個段;是以段越多,搜尋也就越慢。
Elasticsearch通過在背景進行段合并來解決這個問題。小的段被合并到大的段,然後這些大的段再被合并到更大的段。
段合并的時候會将那些舊的已删除文檔 從檔案系統中清除。 被删除的文檔(或被更新文檔的舊版本)不會被拷貝到新的大段中。
啟動段合并不需要你做任何事。進行索引和搜尋時會自動進行。
1、 當索引的時候,重新整理(refresh)操作會建立新的段并将段打開以供搜尋使用。
2、 合并程序選擇一小部分大小相似的段,并且在背景将它們合并到更大的段中。這并不會中斷索引和搜尋。
兩個送出的段和兩個未送出的段被合并到大段裡面
Two commited segments and one uncommited segment in the process of being merged into a bigger segment
一旦合并結束,老的段被删除
合并大的段需要消耗大量的I/O和CPU資源,如果任其發展會影響搜尋性能。Elasticsearch在預設情況下會對合并流程進行資源限制,是以搜尋仍然 有足夠的資源很好地執行
optimize API(強制合并 API )
它會将一個分片強制合并到 max_num_segments 參數指定大小的段數目。 這樣做的意圖是減少段的數量(通常減少到一個),來提升搜尋性能。
例如在日志這種用例下,每天、每周、每月的日志被存儲在一個索引中。 老的索引實質上是隻讀的;它們也并不太可能會發生變化,使用optimize優化老的索引,将每一個分片合并為一個單獨的段就很有用了;這樣既可以節省資源,也可以使搜尋更加快速
合并索引中的每個分片為一個單獨的段:
POST /logstash-2014-10/_optimize?max_num_segments=1