系列目錄
內存吞金獸(Elasticsearch)的那些事兒 -- 認識一下
內存吞金獸(Elasticsearch)的那些事兒 -- 數據結構及巧妙算法
內存吞金獸(Elasticsearch)的那些事兒 -- 架構&三高保證
內存吞金獸(Elasticsearch)的那些事兒 -- 寫入&檢索原理
內存吞金獸(Elasticsearch)的那些事兒 -- 常見問題痛點及解決方案
1、大數據量的查詢效率如何保證:
查詢的流程:往 ES 里寫的數據,實際上都寫到磁盤文件里去了,查詢的時候,操作系統會將磁盤文件里的數據自動緩存到 Filesystem Cache 里面去
最佳的情況下,就是機器的內存,至少可以容納總數據量的一半,僅僅在 es 中就存少量的數據,就是要用來搜索的那些索引,如果內存留給 filesystem cache 的是 100G,那么將索引數據控制在 100G 以內,這樣的話,數據幾乎全部走內存來搜索,性能非常之高,一般可以在 1 秒以內,但是生成環境的數據量往往還是會很多,有大致四種方案:
1)數據預熱
平時看的人很多的數據,每隔一會兒,去搜索一下熱數據,刷到 filesystem cache
里去,后面用戶實際上來看這個熱數據的時候,就是直接從內存里搜索了;
2)冷熱分離
將大量的訪問很少、頻率很低的數據,單獨寫一個索引,然后將訪問很頻繁的熱數據單獨寫一個索引。最好是將冷數據寫入一個索引中,然后熱數據寫入另外一個索引中,這樣可以確保熱數據在被預熱之后,盡量都讓他們留在 filesystem os cache
里,別讓冷數據給沖刷掉。
3)es+Hbase架構:
es只存儲索引字段,其他數據放到mysql/hbase中;
舉例說明:id,name,age .... 30 個字段。現在搜索,只需要根據 id,name,age 三個字段來搜索。如果往 es 里寫入一行數據所有的字段,就會導致說 90% 的數據是不用來搜索的,結果硬是占據了 es 機器上的 filesystem cache 的空間,單條數據的數據量越大,就會導致 filesystem cahce 能緩存的數據就越少。其實,僅僅寫入 es 中要用來檢索的少數幾個字段就可以了,比如說就寫入es id,name,age 三個字段,然后你可以把其他的字段數據存在 mysql/hbase 里,我們一般是建議用 es + hbase 這么一個架構。
4)document 模型設計
對於 MySQL,我們經常有一些復雜的關聯查詢。es 里面的復雜的關聯查詢盡量別用,一旦用了性能一般都不太好。
要先在 Java 系統里就完成關聯,將關聯好的數據直接寫入 es 中。搜索的時候,就不需要利用 es 的搜索語法來完成 join 之類的關聯搜索了。
2、分頁查詢痛點及解決方案:
假設現在要查詢第100頁的10條數據,但是對於es來說,from=1000000,size=100,這時 es需要從各個分片上查詢出來10000100條數據,然后匯總計算后從其中取出100條。如果有5個分片則需要查詢出來5*10000100條數據,如果現在有並發的100個查詢請求,就會有50億左右的數據,占用的內存是非常高的,所以在使用es的分頁查詢過程中,剛開始翻頁可能速度比較快,可能到第一百頁查詢就需要4-5s,翻到1000頁以后,系統資源占用成指數級上升,很容易就會出現OOM直接報錯。
分頁方案:
1)基本的from-size查詢,es為了避免深度分頁帶來的內存開銷,from最大值設定到了10000,目前后台運營的翻頁最多關心近10頁的數據;
2)search after按照第一個檢索到的最后顯示的“balance”和‘_id’值,作為下一個檢索search_after的參數,例如假定size是10,當查詢990-1000時,通過上次傳遞的最后一個檢索到的值,在分片上就可以取到10條文檔,不支持上一頁查詢。
3)scroll查詢
scroll查詢原理是在第一次查詢的時候一次性生成一個快照,根據上一次的查詢的id來進行下一次的查詢,這個就類似於關系型數據庫的游標,然后每次滑動都是根據產生的游標id進行下一次查詢,這種性能比上面說的分頁性能要高出很多,基本都是毫秒級的。
注意點:scroll不支持跳頁查詢。
使用場景:對實時性要求不高的查詢。
代碼:
設置查詢條件
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
.must(builder);
|
首次查詢
- 第一次查詢,跟平時的search查詢一樣需要設置index和type以及查詢條件。
- 如果把查詢類型設置成SCAN,那么不能獲取結果並且不支持排序,只能獲得scrollId,如果使用默認設置或者不設置,那么第一次在獲取id的同時也可以獲取到查詢結果。
- 這個size大小的意思不是總分頁的大小,實際數量應該是:所以實際返回的數量是:分片的數量*size
- 滾動時間設置是指在這個查詢搜索結果的緩存時間,時間不能太久,畢竟內存空間是有限的。
.setQuery(boolQueryBuilder)
.setSearchType(.setSearchType(SearchType.DEFAULT))
.execute().actionGet();
//第一次查詢
for
(SearchHit searchHit : response1.getHits().hits()) {
biz handle....;
}
|
第二次查詢
}
response1 = client.prepareSearchScroll(response1.getScrollId()).setScroll(TimeValue.timeValueMinutes(
5
))
.execute().actionGet();
}
|
4) 利用scroll-scan遍歷數據
使用場景:500w用戶,需要遍歷所有用戶發送數據,並且對順序沒有要求,這個時候我們可以使用scroll-scan。
查詢
.setQuery(boolQueryBuilder)
.setSearchType(SearchType.SCAN)
.execute().actionGet();
獲取結果
SearchResponse response1 = client.prepareSearchScroll(scrollId).setScroll(TimeValue.timeValueMinutes(
5
))
.execute().actionGet();
}
response1 = client.prepareSearchScroll(response1.getScrollId()).setScroll(TimeValue.timeValueMinutes(
5
))
.execute().actionGet();
}
|
scroll和scroll-scan區別
-
scroll支持排序,scroll-scan不支持排序,是按照索引順序返回,可以提高查詢效率。
-
scroll-scan第一次查詢只支持返回id,沒有結果。
總結:
- es的分頁查詢不支持深度分頁,如果偏要使用要結合具體業務場景進行使用。不能當成關系型數據庫中的分頁進行使用。
- 要想提高產品體驗和查詢效率不能過於依賴技術,要結合需求進行分析以提高體驗,因為很多搜索類產品都不支持深度分頁。
- 如果在不涉及排序的情況下盡量使用scroll-scan,它是按照索引順序返回,提高效率。