ElasticSearch最佳實踐

es 第一是準實時的,數據寫入 1 秒後可以搜索到;可能會丟失數據的。有 5 秒的數據,停留在 buffer、translog os cache、segment file os cache 中,而不在磁盤上,此時如果宕機,會導致 5 秒的數據丟失。

歸根結底,你要讓 es 性能要好,最佳的情況下,就是你的機器的內存,至少可以容納你的總數據量的一半。

根據我們自己的生產環境實踐經驗,最佳的情況下,是僅僅在 es 中就存少量的數據,就是你要用來搜索的那些索引,如果內存留給 filesystem cache 的是 100G,那麼你就將索引數據控制在 100G 以內,這樣的話,你的數據幾乎全部走內存來搜索,性能非常之高,一般可以在 1 秒以內。

比如說你現在有一行數據。id,name,age .... 30 個字段。但是你現在搜索,只需要根據 id,name,age 三個字段來搜索。如果你傻乎乎往 es 裏寫入一行數據所有的字段,就會導致說 90% 的數據是不用來搜索的,結果硬是佔據了 es 機器上的 filesystem cache 的空間,單條數據的數據量越大,就會導致 filesystem cahce 能緩存的數據就越少。其實,僅僅寫入 es 中要用來檢索的少數幾個字段就可以了,比如說就寫入es id,name,age 三個字段,然後你可以把其他的字段數據存在 mysql/hbase 裏,我們一般是建議用 es + hbase 這麼一個架構。

hbase 的特點是適用於海量數據的在線存儲,就是對 hbase 可以寫入海量數據,但是不要做複雜的搜索,做很簡單的一些根據 id 或者範圍進行查詢的這麼一個操作就可以了。從 es 中根據 name 和 age 去搜索,拿到的結果可能就 20 個 doc id,然後根據 doc id 到 hbase 裏去查詢每個 doc id 對應的完整的數據,給查出來,再返回給前端。

寫入 es 的數據最好小於等於,或者是略微大於 es 的 filesystem cache 的內存容量。然後你從 es 檢索可能就花費 20ms,然後再根據 es 返回的 id 去 hbase 裏查詢,查 20 條數據,可能也就耗費個 30ms,可能你原來那麼玩兒,1T 數據都放es,會每次查詢都是 5~10s,現在可能性能就會很高,每次查詢就是 50ms。

數據預熱

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

其實可以做數據預熱。

舉個例子,拿微博來說,你可以把一些大V,平時看的人很多的數據,你自己提前後臺搞個系統,每隔一會兒,自己的後臺系統去搜索一下熱數據,刷到 filesystem cache 裏去,後面用戶實際上來看這個熱數據的時候,他們就是直接從內存裏搜索了,很快。

或者是電商,你可以將平時查看最多的一些商品,比如說 iphone 8,熱數據提前後臺搞個程序,每隔 1 分鐘自己主動訪問一次,刷到 filesystem cache 裏去。

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

document 模型設計

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

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

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

 

分頁性能優化

es 的分頁是較坑的,爲啥呢?舉個例子吧,假如你每頁是 10 條數據,你現在要查詢第 100 頁,實際上是會把每個 shard 上存儲的前 1000 條數據都查到一個協調節點上,如果你有個 5 個 shard,那麼就有 5000 條數據,接着協調節點對這 5000 條數據進行一些合併、處理,再獲取到最終第 100 頁的 10 條數據。

分佈式的,你要查第 100 頁的 10 條數據,不可能說從 5 個 shard,每個 shard 就查 2 條數據?最後到協調節點合併成 10 條數據?你必須得從每個 shard 都查 1000 條數據過來,然後根據你的需求進行排序、篩選等等操作,最後再次分頁,拿到裏面第 100 頁的數據。你翻頁的時候,翻的越深,每個 shard 返回的數據就越多,而且協調節點處理的時間越長,非常坑爹。所以用 es 做分頁的時候,你會發現越翻到後面,就越是慢。

我們之前也是遇到過這個問題,用 es 作分頁,前幾頁就幾十毫秒,翻到 10 頁或者幾十頁的時候,基本上就要 5~10 秒 才能查出來一頁數據了。

有什麼解決方案嗎?

不允許深度分頁(默認深度分頁性能很差)

跟產品經理說,你係統不允許翻那麼深的頁,默認翻的越深,性能就越差。

類似於 app 裏的推薦商品不斷下拉出來一頁一頁的

類似於微博中,下拉刷微博,刷出來一頁一頁的,你可以用 scroll api,關於如何使用,自行上網搜索。

scroll 會一次性給你生成所有數據的一個快照,然後每次滑動向後翻頁就是通過遊標 scroll_id 移動,獲取下一頁下一頁這樣子,性能會比上面說的那種分頁性能要高很多很多,基本上都是毫秒級的。

但是,唯一的一點就是,這個適合於那種類似微博下拉翻頁的,不能隨意跳到任何一頁的場景。也就是說,你不能先進入第 10 頁,然後去第 120 頁,然後又回到第 58 頁,不能隨意亂跳頁。所以現在很多產品,都是不允許你隨意翻頁的,app,也有一些網站,做的就是你只能往下拉,一頁一頁的翻。

初始化時必須指定 scroll 參數,告訴 es 要保存此次搜索的上下文多長時間。你需要確保用戶不會持續不斷翻頁翻幾個小時,否則可能因爲超時而失敗。

除了用 scroll api,你也可以用 search_after 來做,search_after 的思想是使用前一頁的結果來幫助檢索下一頁的數據,顯然,這種方式也不允許你隨意翻頁,你只能一頁頁往後翻。初始化時,需要使用一個唯一值的字段作爲 sort 字段。

 

  • es 生產集羣我們部署了 5 臺機器,每臺機器是 6 核 64G 的,集羣總內存是 320G。
  • 我們 es 集羣的日增量數據大概是 2000 萬條,每天日增量數據大概是 500MB,每月增量數據大概是 6 億,15G。目前系統已經運行了幾個月,現在 es 集羣裏數據總量大概是 100G 左右。
  • 目前線上有 5 個索引(這個結合你們自己業務來,看看自己有哪些數據可以放 es 的),每個索引的數據量大概是 20G,所以這個數據量之內,我們每個索引分配的是 8 個 shard,比默認的 5 個 shard 多了 3 個shard。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章