ES 億級數據毫秒級性能優化?

 

  1. ES 簡介

  2. 問題與剖析

  3. 搜索性能優化

    1. 性能的關鍵:filesystem cache

    2. 三人行必有我師(性能優化)

 

 

 

1、簡介


 

    ES是一個基於RESTful web接口並且構建在Apache Lucene之上的開源分佈式搜索引擎。

 

    同時ES還是一個分佈式文檔數據庫,其中每個字段均可被索引,而且每個字段的數據均可被搜索,能夠橫向擴展至數以百計的服務器存儲以及處理PB級的數據。

 

    可以在極短的時間內存儲、搜索和分析大量的數據。通常作爲具有複雜搜索場景情況下的核心發動機。

 

2、問題與剖析


 

問題:

       es 在數據量很大的情況下(數十億級別)查詢一定快嗎,如何提高查詢效率啊?

 

剖析:

    crud 是所有的數據存儲服務的基本操作,無論是 NoSQL、SQL數據庫、文件存儲服務...... 既然是基本操作,就有一個大家都會遇到的問題:性能。

    有朋友說,ES 本身的架構設計就是爲了解決大批量的數據檢索問題,他的查詢一定快,我們只管查就對了。

    個人認爲,他說的對, 對於一些只進行 CRUD 的朋友來說,這已經夠了。但是,我們日常的工作並不是只做 CRUD,我們需要設計,設計索引,優化索引,優化查詢,這就不夠了。知其然,需知其所以然,直到他爲什麼快,直到的快的關鍵點(瓶頸)在哪裏,才能更好的設計與使用。

    

    那麼,這個問題的答案就很簡單了,答案是:不一定快,或者說,肯定會有慢的時候。就像MySQL 查詢一樣,肯定有一個瓶頸。

 

    重點來了,瓶頸在哪裏?如何優化(解決)瓶頸??

    

 

3、搜索性能優化


 

a. 性能的關鍵:filesystem cache


    持久化,是大部分的數據存儲服務都具備的特性之一,而且,幾乎所有的持久化信息最終都會落到磁盤中,ES 也不例外。

 

ES 檢索快的原因有兩點:

           1、磁盤存儲的數據結構 - 倒排索引

           2、檢索機制 - filesystem cache

 

    第一點,暫時不說,相對複雜一些,牽扯到分詞內容,我們先忽略第一點。

 

 

   在 ES 的查詢過程中,依賴了底層的 filesystem cache 。也就是,先查  filesystem cache ,查到了直接返回;查不到時,查詢磁盤文件並緩存到  filesystem cache  中,然後返回。

    看到這裏,是不是感覺很熟悉,像不像 Redis - MySQL 的使用方式:先查緩存,然後查數據庫,同時緩存數據。

    從此,可以看出,性能的關鍵就在緩存能放下多少數據。那麼,緩存能放下多少數據和由什麼決定呢?感覺是不是抓住了點什麼,嘻嘻,由內存決定。

 

    舉慄說明:假設我們有三臺服務器,內存是32G ,es 的jvm heap 設置爲 16G(用於計算),留給緩存 filesystem cache 的只剩下了 16G(可能還不夠16G,爲了方便計算,用16G 進行說明)。此時,三臺機器能夠緩存的數據量 =  3 * 16G = 48G,也就是說,只有這 48G 的數據查詢數據快,其他的都要從磁盤文件獲取,那麼,性能也就降下來了。

    

    總結:要想性能好,內存必選大。至少保證達到整體數據量的某一個百分比,eg:50%。

 

    有人說,不合理啊,實際情況下我們不可能知道或者說決定機器的內存有多大的。也不能保證達到整體數據量的某一個百分比。

 

b. 三人行必有我師(性能優化)


 

    子曰:三人行,必有我師。既然都是數據存儲服務,我們就可以參考同行的案例啊。

    同行給了我們三個方面的優化方式

 

數據存儲優化


 

    一句話,節約 filesystem cache 的信息量,能不放 es 的就不放 es,es 只放關鍵數據。eg:id、name、age等。

    結合 MySQL、Hbase 、Redis 進行查詢。從 ES 中獲取主要數據 ID ,通過ID 查詢完整信息。ES 的查詢可能要 20 ms,完整信息的獲取需要40ms,整個查詢過程只需要 60ms。這樣是不是更快了呢??

    有一個問題考慮,如果數據牽扯到了分庫分表,那麼我們要將路由關鍵字記錄在 ES 的索引中。

 

冷熱數據、數據預熱


    

    es 可以做類似於 mysql 的水平拆分,就是說將大量的訪問很少、頻率很低的數據,單獨寫一個索引,然後將訪問很頻繁的熱數據單獨寫一個索引。最好是將冷數據寫入一個索引中,然後熱數據寫入另外一個索引中,這樣可以確保熱數據在被預熱之後,儘量都讓他們留在 filesystem os cache 裏,別讓冷數據給沖刷掉。

    你看,假設你有 6 臺機器,2 個索引,一個放冷數據,一個放熱數據,每個索引 3 個 shard。3 臺機器放熱數據 index,另外 3 臺機器放冷數據 index。然後這樣的話,你大量的時間是在訪問熱數據 index,熱數據可能就佔總數據量的 10%,此時數據量很少,幾乎全都保留在 filesystem cache 裏面了,就可以確保熱數據的訪問性能是很高的。但是對於冷數據而言,是在別的 index 裏的,跟熱數據 index 不在相同的機器上,大家互相之間都沒什麼聯繫了。如果有人訪問冷數據,可能大量數據是在磁盤上的,此時性能差點,就 10% 的人去訪問冷數據,90% 的人在訪問熱數據,也無所謂了。

 

    假如說,哪怕是你就按照上述的方案去做了,es 集羣中每個機器寫入的數據量還是超過了 filesystem cache 一倍,比如說你寫入一臺機器 60G 數據,結果 filesystem cache 就 30G,還是有 30G 數據留在了磁盤上。

    其實可以做數據預熱。

 

    對於那些你覺得比較熱的、經常會有人訪問的數據,最好做一個專門的緩存預熱子系統,就是對熱數據每隔一段時間,就提前訪問一下,讓數據進入 filesystem cache 裏面去。這樣下次別人訪問的時候,性能一定會好很多。

 

 

索引 ducument 設計


    對於 MySQL,我們經常有一些複雜的關聯查詢。在 es 裏該怎麼玩兒,es 裏面的複雜的關聯查詢儘量別用,一旦用了性能一般都不太好。

    最好是先在 Java 系統裏就完成關聯,將關聯好的數據直接寫入 es 中。搜索的時候,就不需要利用 es 的搜索語法來完成 join 之類的關聯搜索了。

    document 模型設計是非常重要的,很多操作,不要在搜索的時候纔想去執行各種複雜的亂七八糟的操作。es 能支持的操作就那麼多,不要考慮用 es 做一些它不好操作的事情。如果真的有那種操作,儘量在 document 模型設計的時候,寫入的時候就完成。另外對於一些太複雜的操作,比如 join/nested/parent-child 搜索都要儘量避免,性能都很差的。

    

分頁性能優化


 

    不要做深度分頁,在es 中默認查詢上限是10000 條數據。如果超過一萬條不返回。

    這是一個很大的坑。爲什麼這樣呢?

    原因:分頁查詢的查詢機制。client 請求查詢100 條數據,接受請求節點(node 1)接收請求,node 1 向所有的節點索要數據,每一個節點都給 100條數據,然後由node 1 來進行排序獲取,也就是拿到最終的100條數據。第一頁如此,第二頁也是如此。所有我們每次分頁的開銷是比較大的。所以慢慢的分頁查詢也就會越來越慢。

    

    如果需要獲取深度的分頁數據,可以通過 scroll API(遊標查詢) 來解決,這裏就不多說了,下一篇文章會詳細介紹,嘻嘻。

          

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