如何優化100s的Elasticsearch 查詢到1s之內

在SQL的世界裏, 查詢優化是至關成熟而且是能夠理解的, 另一方面, 分佈式數據庫系統是新出現的, 而且不太成熟. 理解查詢是如何工做的將是一件很是重要的事情.html

Elstaticsearch的查詢有時候將超過100s, 從而引起了不少timeout(超時), GC(垃圾回收) ,cache(緩存)更新等問題, 這裏咱們列出了在分析過程當中發現的幾個有意思的問題, 咱們是如何優化到1s之內的, 下面也會給出答案.數據庫

在最慢的時候, 咱們的請求量大約有150,000每秒 輸入圖片說明緩存

大量的請求並非問題, 以下圖片顯示了filter cache的大小變化過程, 當前咱們環境中有11個Elasticsearch的結點, 每一個結點實例有30G內存, 總計整個集羣有330G的內存, 40%的內存貢獻給了 filter cache, 當咱們執行查詢的時候, 查詢結果就緩存在這裏, 查詢結果的重複使用極大地提高了查詢性能. 輸入圖片說明負載均衡

內存空間是被全部消費者共享使用的, 而且受最近最少執行計劃影響, 當咱們增長一塊filter cache內存空間時, 最近最少執行的查詢所對應的filter cache就會被踢出去. 輸入圖片說明elasticsearch

從上面的圖能夠看出, 一次次內存空間的週期波動,致使了一些超長GC的執行.分佈式

輸入圖片說明

任何內存的踢出致使了無數週期性老年代垃圾回收. 老年代GC致使了執行過程的暫停, 這意味着Elasticsearch 每一個結點在GC的過程當中, 對於集羣中的其它結點, 狀態是死亡的, 不會接受任何請求, 即便是集羣的的請求. 理想狀況下老年代GC的應該是少見和短期的. 這裏咱們看到的許多結點的GC是頻繁的和長時間的.ide

咱們的CPU使用率或disk i/o 是沒有壓力的, 主要是內存的約束. 集羣結點有64G的內存, 30G分配給了elasticsearch JVM, 剩下的分配給了file cache, 因爲file cache使用了SSD, 咱們發現4s不到的時間, SSD寫入了132G的數據, 這致使了Out of memory 異常, 而且該結點的異常致使了集羣的崩潰.性能

  1. 升級內存

升級硬件並不老是一個正確的方案, 可是從咱們的案例來看, 主要受到內存的限制, 咱們能夠在結點上增長一倍的內存. 可是咱們不建議在JVM heap上分配的內存大於32G, 所以我決定在每一個結點增長一倍內存的基礎上運行2個Elasticsearch實例,感謝Elasticsearch已經意識到主,備分區(shard)不能同時運行是同一個盒子(box), 時間將驗證內存升級後的運行狀況.優化

  1. 控制什麼數據被緩存

咱們的第一直覺就是檢查什麼數據被緩存了, 當檢查咱們的查詢語句才發現, 好像全部的東西都被緩存了, 緩存的數據太多了, 以下語句是咱們查詢語句中的一個: 輸入圖片說明ui

只須要緩存查詢語句中的過濾條件, 其它不用管, 這和咱們指望的差很少.

  1. 改變查詢語句

查詢慢的時候, 咱們集羣中的文檔數量大約有64,000,000. 請求須要作Map-Reduce的工做, 請求從負載均衡的客戶端向分佈式集羣中的全部結點請求數據, 結點收到請求後分發請求到全部分區(shard), 每一個分區(shard)緩存過濾條件到集合中. 輸入圖片說明

從第一次請求的過濾條件被緩存以後, 後面全部的查詢都從緩存的內存的讀取數據, 可是這裏有個問題, 因爲數據太多的緣由, 請求有可能被路由到主結點或者備份結點, 過濾器緩存被不斷的刷新並重建, 爲了減小請求查詢數量, 咱們來看看聚合查詢是如何工做的.

  1. 聚合查詢

輸入圖片說明

Elasticsearch 支持不少聚合查詢, 例如上圖中的短語(terms)聚合查詢, 和SQL中的group by很像, 可是Elasticsearch 的數據分佈在多個分區(shard), 當一個聚合查詢執行時, 全部分區(shard)都會收到請求並返回數據視圖, 結點收到返回的數據, 完成聚合加工除理後將數據返回給客戶端, 因爲分佈式數據的緣由, 計算結果並非絕對精確, Elasticsearch 是如何工做的官方文檔以下: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html

Elasticsearch支持分桶後的嵌套分桶. 輸入圖片說明

上面的查詢, 首先文檔經過 gender 分組並完成了avg_height的計算, 這個查詢在跨集羣中的全部結點機器並行計算, 因爲查詢下發到了每一個結點, 減小了內存使用的壓力.

可是由於須要作聚合計算, 全部的字段數據必須加載到內存中, Elasticsearch在field cache中緩存字段數據, 默認狀況下JVM heap中的10%分配給field cache, 隨着聚合的不斷使用, 不得不緩存幾乎全部的字段到內存中, 因爲咱們不能預測須要多大的內存, 這很容易致使內存溢出(Out of memeory)的異常, 這也直接致使了內存上的壓力, 引起了更多的老年代垃圾回收, 查詢也會變慢, 甚至發生集羣崩潰的危險.

爲了不這類事情的發生, Elasticsearch 對field cache所佔用的總內存有回收中斷的保護措施, 這能夠在請求層面設置, 當請求消耗的內存達到設置值時, 請求會被停止. 默認狀況下, field cache使用了延遲加載, 除此以外, Elastsicsearch的處理方式還有不少, 也有經過文件系統緩存field data的, 文件系統緩存被操做系統管理, 因爲沒有垃圾回收機制更加高效, 分佈式系統正在慢慢地遠離JVM內存模型, Apache Kafka已經徹底依賴文件系統緩存.

http://www.elasticsearch.org/guide/en/elasticsearch/guide/current/preload-fielddata.html

有時候聚合查詢可能會比通常的查詢慢10倍, 例如當一個小於25個字符的字段作爲terms聚合列查詢時, 普通查詢將比聚合查詢快不少, 極限時, JVM內存使用上也難以接受, Elasticsearch如此之快, 是由於使用了內存, 一樣若是不關心內存的使用, 性能從根本上也會受影響. 沒有必要的狀況下, 不要使用內存, 下降JVM內存的使用壓力, 使用文件系統緩存或者相似的方式.

相關文章
相關標籤/搜索