- 需求說明 -
項目背景:
改進版本目標:
-
數據能跨月查詢,並且支持1年以上的歷史數據查詢與導出。 -
按條件的數據查詢秒級返回。
- 深入原理 -
Elasticsearch檢索原理
3.1 關於ES和Lucene基礎結構
-
Cluster: 包含多個Node的集羣 -
Node: 集羣服務單元 -
Index: 一個ES索引包含一個或多個物理分片,它只是這些分片的邏輯命名空間 -
Type: 一個index的不同分類,6.x後只能配置一個type,以後將移除 -
Document: 最基礎的可被索引的數據單元,如一個JSON串 -
Shards : 一個分片是一個底層的工作單元,它僅保存全部數據中的一部分,它是一個Lucence實例 (一個Lucene: 索引最大包含2,147,483,519 (= Integer.MAX_VALUE - 128)個文檔數量) -
Replicas: 分片備份,用於保障數據安全與分擔檢索壓力 ES依賴一個重要的組件Lucene,關於數據結構的優化通常來說是對Lucene的優化,它是集羣的一個存儲於檢索工作單元,結構如下圖:
3.2 Lucene索引實現
注: 整理來源於lucene官方: http://lucene.apache.org/core/7_2_1/core/org/apache/lucene/codecs/lucene70/package-summary.html#package.description
For other features that we now commonly associate with search, such as sorting, faceting, and highlighting, this approach is not very efficient. The faceting engine, for example, must look up each term that appears in each document that will make up the result set and pull the document IDs in order to build the facet list. In Solr, this is maintained in memory, and can be slow to load (depending on the number of documents, terms, etc.)
3.3 關於ES索引與檢索分片
- 優化案例 -
-
ES僅提供字段的檢索,僅存儲HBase的Rowkey不存儲實際數據。 -
實際數據存儲在HBase中,通過Rowkey查詢,如下圖。 -
提高索引與檢索的性能建議,可參考官方文檔(如 https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-indexing-speed.html)。
4.1 優化索引性能
-
批量寫入,看每條數據量的大小,一般都是幾百到幾千。 -
多線程寫入,寫入線程數一般和機器數相當,可以配多種情況,在測試環境通過Kibana觀察性能曲線。 -
增加segments的刷新時間,通過上面的原理知道,segment作爲一個最小的檢索單元,比如segment有50個,目的需要查10條數據,但需要從50個segment分別查詢10條,共500條記錄,再進行排序或者分數比較後,截取最前面的10條,丟棄490條。在我們的案例中將此 "refresh_interval": "-1" ,程序批量寫入完成後進行手工刷新(調用相應的API即可)。 -
內存分配方面,很多文章已經提到,給系統50%的內存給Lucene做文件緩存,它任務很繁重,所以ES節點的內存需要比較多(比如每個節點能配置64G以上最好)。 -
磁盤方面配置SSD,機械盤做陣列RAID5 RAID10雖然看上去很快,但是隨機IO還是SSD好。 -
使用自動生成的ID,在我們的案例中使用自定義的KEY,也就是與HBase的ROW KEY,是爲了能根據rowkey刪除和更新數據,性能下降不是很明顯。 -
關於段合併,合併在後臺定期執行,比較大的segment需要很長時間才能完成,爲了減少對其他操作的影響(如檢索),elasticsearch進行閾值限制,默認是20MB/s,可配置的參數:"indices.store.throttle.max_bytes_per_sec" : "200mb" (根據磁盤性能調整)合併線程數默認是:Math.max(1, Math.min(4, Runtime.getRuntime().availableProcessors() / 2)),如果是機械磁盤,可以考慮設置爲1:index.merge.scheduler.max_thread_count: 1,在我們的案例中使用SSD,配置了6個合併線程。
4.2 優化檢索性能
-
關閉不需要字段的doc values。 -
儘量使用keyword替代一些long或者int之類,term查詢總比range查詢好 (參考lucene說明 http://lucene.apache.org/core/7_4_0/core/org/apache/lucene/index/PointValues.html)。 -
關閉不需要查詢字段的_source功能,不將此存儲僅ES中,以節省磁盤空間。 -
評分消耗資源,如果不需要可使用filter過濾來達到關閉評分功能,score則爲0,如果使用constantScoreQuery則score爲1。 -
關於分頁:
-
from + size: 每分片檢索結果數最大爲 from + size,假設from = 20, size = 20,則每個分片需要獲取20 * 20 = 400條數據,多個分片的結果在協調節點合併(假設請求的分配數爲5,則結果數最大爲 400*5 = 2000條) 再在內存中排序後然後20條給用戶。這種機制導致越往後分頁獲取的代價越高,達到50000條將面臨沉重的代價,默認from + size默認如下:index.max_result_window :10000 -
search_after: 使用前一個分頁記錄的最後一條來檢索下一個分頁記錄,在我們的案例中,首先使用from+size,檢索出結果後再使用search_after,在頁面上我們限制了用戶只能跳5頁,不能跳到最後一頁。 -
scroll 用於大結果集查詢,缺陷是需要維護scroll_id
{
"mappings": {
"data": {
"dynamic": "false",
"_source": {
"includes": ["XXX"] -- 僅將查詢結果所需的數據存儲僅_source中
},
"properties": {
"state": {
"type": "keyword", -- 雖然state爲int值,但如果不需要做範圍查詢,儘量使用keyword,因爲int需要比keyword增加額外的消耗。
"doc_values": false -- 關閉不需要字段的doc values功能,僅對需要排序,匯聚功能的字段開啓。
},
"b": {
"type": "long" -- 使用了範圍查詢字段,則需要用long或者int之類 (構建類似KD-trees結構)
}
}
}
},
"settings": {......}
}
- 性能測試 -
-
單節點5千萬到一億的數據量測試,檢查單點承受能力。 -
集羣測試1億-30億的數量,磁盤IO/內存/CPU/網絡IO消耗如何。 -
隨機不同組合條件的檢索,在各個數據量情況下表現如何。 -
另外SSD與機械盤在測試中性能差距如何。
- 生產效果 -
Flink CDC我喫定了耶穌也留不住他!| Flink CDC線上問題小盤點
4萬字長文 | ClickHouse基礎&實踐&調優全視角解析
你好,我是王知無,一個大數據領域的硬核原創作者。
做過後端架構、數據中間件、數據平臺&架構、算法工程化。
專注大數據領域實時動態&技術提升&個人成長&職場進階,歡迎關注。
本文分享自微信公衆號 - 大數據技術與架構(import_bigdata)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。