技術分享丨Elasticsearch實操乾貨分享

技術分享丨Elasticsearch實操乾貨分享

導讀

ElasticSearch是基於Lucene的搜索服務器。它是提供了分佈式多用戶能力的全文搜索引擎。Elasticsearch提供了強大的搜索、集合功能。這裡主要想跟大家探討下Elasticsearch索引結構設計的優化以及Elasticsearch在易觀鋯雲產品中的實踐經驗。

一、概述

Elasticsearch提供了豐富的數據類型,支持各種複雜的數據結構和複雜的業務場景。為了能夠實現更快的索引速度和查詢速度,在設計索引結構時,會考慮儘量減少字段數,減小文檔大小,縮短索引過程,以期提高檢索的速度。另外也會結合業務場景在不同數據結構之間要有所取捨。

二、優化策略

1._id生成策略

_id的生成策略對索引速度影響非常大。索引數據時,如果指定_id,Elasticsearch的版本檢查機制會校驗該_id是否存在,並生成新的版本號,默認先從緩存中取版本信息。如緩存中不存在,則要執行一次檢索,隨著數據量的增加,該過程的耗時將逐步增加,寫入速度逐步下降。如果不指定_id,使用Elasticsearch自增ID,可以跳過版本檢查機制,大大提高索引速度。但是,這種場景主要用於不可變數據導入的場景,例如日誌導入。而對於大多數業務場景,都需要指定_id,在很多情況下,需要使用文檔中一個或多個字段拼接生成唯一字符串,作為_id。這種情況下,能很好地保證數據的唯一性,但是,無規則的字符串作為_id會明顯拖累索引速度,根據測試結果,純數字的_id,效率最高。

2. text or keyword

Elasticsearch中,字符串類型可以選擇text和keyword,text類型在寫入數據時,會被分詞。因此,如果沒有分詞查詢的需要,字符串類型一律使用keyword類型更為高效。

3. number or keyword

我們並非一定要用數值類型的字段(byte、integer、long...)存儲數字,Elasticsearch在索引數據的時候,對不同數據類型做了不同優化以便於應對不同檢索場景,對數值類型,提高了range query的查詢速度,而對keyword類型,提高了term query 的查詢速度. 因而在設計索引的時,針對數字,如各種ID,若不存在range query的情況,優先使用keyword類型。

4. 儘量避免join

作為全文檢索的搜索引擎,join查詢,一直是Elasticsearch的弱項。但在日常業務中,有些情況下,又不可避免的需要使用join查詢。Elasticsearch 提供了nested、parent-child兩種關聯查詢的方式。但不幸的是,這兩種查詢方式極大地降低了查詢效率,nested會是查詢效率下降基本,而會產生幾十上百倍的下降。在doc數較小,併發較小的情況下,這種損失可以承受,但是對於億級甚至十億級的集群,這種性能損失是無法忍受的,另外,使用關聯查詢,索引結構變得複雜,索引數據的速度同樣受到影響,nested結構的文檔無法進行索引排序(index sorting),而這種方式可以很好的提高檢索性能。

5.慎用doc_values

Elasticsearch使用倒排索引存儲數據,能夠提供極高的檢索性能,但有得必有失,這種結構並不能帶來很好的排序性能。Elasticsearch提供了doc_values功能,實現對倒排索引的裝置,這是一種列式存儲,能夠提供高效的排序和聚合,默認情況下,Elasticsearch會個每個字段開啟doc_values,如果某個字段並沒有排序和聚合的需求,可以禁用該字段的doc_values屬性,以減少索引數據和存儲的性能消耗。

6._all 字段的使用

_all字段是把其它字段值當作一個大字符串來索引的特殊字段,query_string 查詢子句(搜索 ?q=john )在沒有指定字段時默認使用 _all 字段,這種設置有利於全文檢索,在6.0.0以前的版本里,_all字段默認開啟。實際使用過程中,我們並不一定需要這麼大的字段,甚至不需要這個字段。根據業務需求,我們可以考慮禁用_all字段,或者使用使用copy_to指定字段拼接。

7._source 字段的優化

_source字段存儲了原始json文檔,默認包含所有字段。雖然在獲取文檔字段或整個文檔內容時,非常高效,但同樣也佔用了大量存儲空間。如果某些字段只需要進行檢索,無需返回值,那麼可以通過在_soucrce 中配置includes和excludes來自定義字段存儲,節省存儲空間。

8.dynamic mapping的使用

Elasticsearch默認允許在索引數據的時候可以動態新增字段,但是這種配置有相當風險。因為Elasticsearch的字段不允許刪除和修改數據類型,一旦在索引數據時出現意外,這可能導致索引結構急速膨脹。而且,為了提高讀寫性能我們需要對很多字段進行特殊設置,建議禁用dynamic mapping功能,或者通過dynamic_templates預設數據類型。

9.更少的索引字段

由於Elasticsearch 的join查詢功能的弱項,通常情況下,每個文檔都包含大量字段,有些字段我們需要用來進行檢索過濾,有些字段僅僅用來存儲數據。因而在設計索引的時候,對於無需檢索的字段可以設置enable=false,這樣只存儲不索引,可以提高索引速度,同時,這種字段可以合併成單個字段。

10.預索引數據

在某些業務場景下,為了提高檢索和聚合的速度,我們可以對某些字段的進行預分組。如年齡字段經常有需要按照年齡段進行檢索或聚合,這種情況下,我們可以在每個文檔增加年齡段這個字段,使用keyword類型,這樣可以大大提高檢索和聚合的速度。

三、易觀鋯雲產品中ES索引的設計

易觀鋯雲面向企業級用戶,提供一站式的第三方數據交易及算力加持服務。基於Elasticsearch提供實時/離線數據服務,數據總量大,文檔字段多,業務複雜,併發高,伴隨著高速寫入,讀寫矛盾嚴重。對此我們做了多方位的優化,盡最大努力提高集群性能,以下是索引結構方面優化總結:

1. 文檔含數十個字段,且多個字段中存儲數十條數據。因業務需要,唯一標識的字段是無規則的字符串。索引數據時,我們對該字段進行了散列轉換、取hash等操作,保證了極低的重複率,也提高了數據索引速度。

2. 大量字段經過標準化處理過後,都使用id存儲,在設計索引結構時,統一使用keyword存儲,提高了檢索和聚合性能;禁用了dynamic mapping,自定義copy_to 。

3. 增加分組字段,如,年齡age按照age_range進行預處理,提高檢索和聚合性能。

4. 在鋯雲產品中,有多個字段,是 id -> [value1,value2...]結構,並且需要進行關聯查詢,就需要使用nested Object結構,這大大降低了數據寫入速度和查詢速度,同時,nested結構使得我們無法在寫入數據時,使用順序存儲(index sorting)。對此,我們將字段合併,避開了object結構,同時,更好的支持了關聯查詢。

如, app_tgi:{id:integer, tgi:float},每個app_tgi字段都要存儲幾十條數據,業務中,需要對id及tgi進行關聯查詢過濾,在實際索引中,我們通過將id和tgi分別定長,然後拼裝成一個整數,如id=1000023,tgi=1.6558 最終生成的app_tgi = 10000230000016558,同時,對app_tgi字段進行索引排序,這樣,在實際使用過程中,如果需要讀檢索 id=id=1000023,tgi> 1.6558,就可以通過 app_tgi > 10000230000016558 && app_tgi < 10000239999999999,nested 查詢轉變成數值的range查詢,大大提高了檢索速度。

綜上所述,針對Elasticsearch索引結構的優化就是讓字段更少,文檔更小,讀寫更快。業務場景不同,取捨不同,鋯雲產品查詢的壓力高於寫入壓力,某些情況下我們犧牲了寫入的性能來獲得更好的查詢性能。Elasticsearch的調優是多方位的,在後續的文章中,我們將逐步介紹易觀在使用Elasticsearch過程中,在服務端、客戶端針對讀、寫、穩定性等各方面的優化經驗。

想看更多,請移步Analysys易觀(ID:enfodesk)微信公眾號。


分享到:


相關文章: