天天看點

elasticsearch 優化

批量送出

當有大量資料送出的時候,建議采用批量送出。比如在做 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