乾貨 | Elasticsearch多表關聯設計指南

0、題記

Elasticsearch多表關聯問題是討論最多的問題之一,如:博客和評論的關係,用戶和愛好的關係。
多表關聯通常指:1對多,或者多對多。
本文以星球問題會出發點,引申出ES多表關聯認知,分析了4種關聯關係的適用場景、優點、缺點,
希望對你有所啓發,爲你的多表關聯方案選型、實戰提供幫助。

1、拋出問題

1.1 星球典型問題

在這裏插入圖片描述

1.2 社區典型問題

在這裏插入圖片描述

1.3 QQ羣典型問題

關係型數據庫中的多表之間的關聯查詢,ES中有什麼好的解決方案?
如果我把關聯關係的表遷移到ES中放到一個type下,文檔結構除了對象之間的嵌套還有什麼好的解決方案?

2、基礎認知

2.1 關係型數據庫

關係數據庫是專門爲關係設計的,有如下特點

  • 可以通過主鍵唯一地標識每個實體(如Mysql中的行)。
  • 實體 規範化 。唯一實體的數據只存儲一次,而相關實體只存儲它的主鍵。只能在一個具體位置修改這個實體的數據。
  • 實體可以進行關聯查詢,可以跨實體搜索。
  • 支持ACID特性,即:單個實體的變化是 原子的 , 一致的 , 隔離的 , 和 持久的 。
  • 大多數關係數據庫支持跨多個實體的 ACID 事務。

關係型數據庫的缺陷

  • 第一:全文檢索有限的支持能力。 這點,postgresql已部分支持,但相對有限。
  • 第二:多表關聯查詢的耗時很長,甚至不可用。之前系統開發中使用過Mysql8個表做關聯查詢,一次查詢等待十分鐘+,基本不可用。

2.2 Elasticsearch

Elasticsearch ,和大多數 NoSQL 數據庫類似,是扁平化的。索引是獨立文檔的集合體。 文檔是否匹配搜索請求取決於它是否包含所有的所需信息和關聯程度。

Elasticsearch 中單個文檔的數據變更是滿足ACID的, 而涉及多個文檔時則不支持事務。當一個事務部分失敗時,無法回滾索引數據到前一個狀態。

扁平化有以下優勢

  1. 索引過程是快速和無鎖的。
  2. 搜索過程是快速和無鎖的。
  3. 因爲每個文檔相互都是獨立的,大規模數據可以在多個節點上進行分佈。

2.3 Mysql VS Elasticsearch

  • mysql才擅長關係管理,而ES擅長的是檢索。

  • Medcl也曾強調:“如果可能,儘量在設計時使用扁平的文檔模型。” Elasticsearch的關聯存儲、檢索、聚合操作勢必會有非常大的性能開銷。
    在這裏插入圖片描述

3 Elasticsearch關聯關係如何存儲

關聯關係仍然非常重要。某些時候,我們需要縮小扁平化和現實世界關係模型的差異。
以下四種常用的方法,用來在 Elasticsearch 中進行關聯數據的管理:

3.1 應用端關聯

這是普遍使用的技術,即在應用接口層面來處理關聯關係。
針對星球問題實踐,

  1. 存儲層面:獨立兩個索引存儲。
  2. 實際業務層面分兩次請求:

第一次查詢返回:Top5中文姓名和成績;
根據第一次查詢的結果,第二次查詢返回:Top5中文姓名和英文姓名;

將第一次查詢結果和第二次查詢結果組合後,返回給用戶。
即:實際業務層面是進行了兩次查詢,統一返回給用戶。用戶是無感知的。

適用場景數據量少的業務場景。
優點:數據量少時,用戶體驗好。
缺點:數據量大,兩次查詢耗時肯定會比較長,影響用戶體驗。

引申場景:關係型數據庫和ES 結合,各取所長。將關係型數據庫全量同步到 ES 存儲,不做冗餘存儲。
如前所述:ES 擅長的是檢索,而 MySQL 才擅長關係管理。所以可以考慮二者結合,使用 ES 多索引建立相同的別名,針對別名檢索到對應 ID 後再回 MySQL 查詢,業務層面通過關聯 ID join 出需要的數據。

3.2 寬表冗餘存儲

對應於官方文檔中的“Data denormalization”,官方直接翻譯爲:“非規範化你的數據”,總感覺規範化是什麼鬼,不好理解。
通俗解釋就是:冗餘存儲,對每個文檔保持一定數量的冗餘數據可以在需要訪問時避免進行關聯。

這點通過logstash 同步關聯數據到ES時,通常會建議:先通過視圖對Mysql數據做好多表關聯,然後同步視圖數據到ES。此處的視圖就是寬表。

針對星球問題實踐:姓名、英文名、成績兩張表合爲一張表存儲。

適用場景:一對多或者多對多關聯。

優點:速度快。因爲每個文檔都包含了所需的所有信息,當這些信息需要在查詢進行匹配時,並不需要進行昂貴的關聯操作。

缺點:索引更新或刪除數據,應用程序不得不處理寬表的冗餘數據;
由於冗餘存儲,導致某些搜索和聚合操作可能無法按照預期工作。

3.3 嵌套文檔(Nested)存儲

Nested類型是ES Mapping定義的集合類型之一,它是比object類型更NB的支持獨立檢索的類型。
舉例:有一個文檔描述了一個帖子和一個包含帖子上所有評論的內部對象評論。可以藉助 Nested 實現。

實踐注意1:當使用嵌套文檔時,使用通用的查詢方式是無法訪問到的,必須使用合適的查詢方式(nested query、nested filter、nested facet等),很多場景下,使用嵌套文檔的複雜度在於索引階段對關聯關係的組織拼裝。

推薦實踐:https://blog.csdn.net/laoyang360/article/details/82950393

實踐注意2

index.mapping.nested_fields.limit 缺省值是50。即:一個索引中最大允許擁有50個nested類型的數據。
index.mapping.nested_objects.limit 缺省值是10000。即:1個文檔中所有nested類型json對象數據的總量是10000。

適用場景:1 對少量,子文檔偶爾更新、查詢頻繁的場景。
如果需要索引對象數組並保持數組中每個對象的獨立性,則應使用嵌套 Nested 數據類型而不是對象 Oject 數據類型。

優點:nested文檔可以將父子關係的兩部分數據(舉例:博客+評論)關聯起來,可以基於nested類型做任何的查詢。
缺點:查詢相對較慢,更新子文檔需要更新整篇文檔。

3.4 父子文檔存儲

注意:6.X之前的版本的父子文檔存儲在相同索引的不同type中。而6.X之上的版本,單索引下已不存在多type的概念。父子文檔Join的都是基於相同索引相同type實現的。

Join類型是ES Mapping定義的類型之一,用於在同一索引的文檔中創建父/子關係。 關係部分定義文檔中的一組可能關係,每個關係是父名稱和子名稱。

實踐參考:https://blog.csdn.net/laoyang360/article/details/79774481

適用場景:子文檔數據量要明顯多於父文檔的數據量,存在1 對多量的關係;子文檔更新頻繁的場景。

舉例:1 個產品和供應商之間是1對N的關聯關係。
當使用父子文檔時,使用has_child 或者has_parent做父子關聯查詢。

優點:父子文檔可獨立更新。
缺點:維護Join關係需要佔據部分內存,查詢較Nested更耗資源。

4 小結

在這裏插入圖片描述

  1. 在Elasticsearch開發實戰中對於多表關聯的設計要突破關係型數據庫設計的思維定式
  2. 不建議在es做join操作,parent-child能實現部分功能,但是它的開銷比較大,如果可能,儘量在設計時使用扁平的文檔模型。
  3. 儘量將業務轉化爲沒有關聯關係的文檔形式,在文檔建模處多下功夫,以提升檢索效率。
  4. Nested&Join父子文選型必須考慮性能問題。 nested 類型檢索使得檢索效率慢幾倍,父子Join 類型檢索會使得檢索效率慢幾百倍。

以上內容,實際官方文檔都有明確的描述。我把內容加上自己的理解,作了精煉和解讀。
再次強調:第一手資料的重要性。但本着“再顯而易見的道理,也有N多人不知道”的原則,一定要讀英文官方文檔,加深認知理解。

Elasticsearch多表關聯你是如何做的呢?歡迎留言寫下您的思考。

參考:
[1] https://www.elastic.co/guide/en/elasticsearch/guide/current/relations.html
[2] rockybean 教程

在這裏插入圖片描述
銘毅天下——Elasticsearch基礎、進階、實戰第一公衆號

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