整個文檔纹磺,摘錄來自"銘毅天下"的博客蕉斜。原始的地址如下:
引入問題
多表關聯(lián)通常是指:1對多洒试,或者多對多關系在ES中的呈現(xiàn)硼莽。
比如:博客和評論的關系庶溶,用戶和愛好的關系,主表和子表的關系懂鸵。
基礎認知
關系型數(shù)據(jù)庫
關系型數(shù)據(jù)庫是專門為關系設計的偏螺,有如下特點:
可以通過主鍵唯一地標識每個實體(如Mysql中的行)。
實體規(guī)范化匆光。唯一實體的數(shù)據(jù)只存儲一次套像,而相關實體只存儲它的主鍵。只能在一個具體位置修改這個實體的數(shù)據(jù)终息。
實體可以進行關聯(lián)查詢夺巩,可以跨實體搜索。
支持AICD特性周崭,即:單個實體的變化是原子的柳譬、一致的、隔離的和持久的续镇。
大多數(shù)關系型數(shù)據(jù)庫支持跨多個實體的ACID事務美澳。
關系型數(shù)據(jù)庫的缺陷:
第一:全文檢索有限的支持能力。這點摸航,postgresql已部分支持制跟,但相對有限。
第二:多表關聯(lián)查詢的耗時很長酱虎,甚至不可用凫岖。之前系統(tǒng)開發(fā)中使用過Mysql8個表做關聯(lián)查詢,一次查詢等待十分鐘以上逢净,甚至不可用。
Elasticsearch
ES和大多數(shù)NoSQL數(shù)據(jù)庫類似,是扁平化的爹土。索引是獨立文檔的集合體甥雕。文檔是否匹配搜索請求取決于它是否包含所有的所需信息和關聯(lián)程度。
ES中單個文檔的數(shù)據(jù)變更是滿足ACID的胀茵,但是如果涉及多個文檔時的刪除社露,修改時,則不支持事務琼娘。當一個事務中的部分文檔更新失敗的時候峭弟,是無法將所有涉及到的事務內(nèi)的文檔更新操作都回滾到之前狀態(tài)的。
扁平化有如下的優(yōu)勢:
索引過程是快速和無鎖的脱拼。
搜索過程是快速和無鎖的瞒瘸。
因為每個文檔相互都是獨立的,大規(guī)模數(shù)據(jù)可以在多個節(jié)點上進行分布熄浓。
Mysql VS Elasticsearch
mysql擅長關系管理情臭,而ES擅長的是檢索。
Medcl也曾強調(diào):"如果可能赌蔑,盡量在設計時使用扁平的文檔模型俯在。"ES的關聯(lián)存儲、檢索娃惯、聚合操作勢必會有非常大的性能開銷跷乐。
ES場景中如何解決這種關聯(lián)關系
關聯(lián)關系仍然非常重要。某些時候趾浅,我們需要縮小扁平化和現(xiàn)實世界關系模型的差異愕提。有如下的四種常用的方法,用來在ES中進行關聯(lián)數(shù)據(jù)的管理潮孽。
應用端關聯(lián)
這是普遍使用的技術揪荣,即在應用接口層面來處理關聯(lián)關系。
針對上面的問題往史,來如下實踐:
1\. 存儲層面:獨立兩個索引存儲
2\. 實際業(yè)務層面分兩次請求:
第一次查詢返回:Top5中文姓名和成績仗颈;
根據(jù)第一次查詢的結果中,取出中文姓名椎例。
到第二個表中進行查詢挨决,返回對應的Top5中文姓名和英文姓名。
最后將第一次查詢結果和第二次查詢結果組合后订歪,返回給用戶脖祈。
即:實際業(yè)務層面是進行兩次查詢,統(tǒng)一返回給用戶刷晋。用戶是無感知的盖高。
適用場景:數(shù)據(jù)量少的業(yè)務場景慎陵。
優(yōu)點:數(shù)據(jù)量少時,用戶體驗好喻奥。
缺點:數(shù)據(jù)量大席纽,兩次查詢耗時肯定會比較長,影響用戶體驗撞蚕。
引申場景:關系型數(shù)據(jù)庫和ES結合润梯,各取所長。將關系型數(shù)據(jù)庫全量同步到ES存儲甥厦,不做冗余處理纺铭。
如前所述:ES擅長的是檢索,而MySQL才擅長關系管理刀疙。所以可以考慮二者結合舶赔,使用ES多索引建立相同的別名,針對別名檢索到對應ID后再回MySQL查詢庙洼,業(yè)務層面通過關聯(lián)ID join 出需要的數(shù)據(jù)顿痪。
寬表冗余存儲
對每個文檔保持一定數(shù)量的冗余數(shù)據(jù)可以在需要訪問時避免進行關聯(lián)。
這點通過logstash同步關聯(lián)數(shù)據(jù)到ES時油够,通常會建議:先通過視圖對MySQL數(shù)據(jù)做好多表關聯(lián)蚁袭,然后同步視圖數(shù)據(jù)到ES。此處的視圖就是寬表石咬。
針對最開始提出的問題:姓名揩悄、英文名、成績兩張表合為一張表存儲鬼悠。
適用場景:一對多或者多對多關聯(lián)删性。
優(yōu)點:速度快。因為每個文檔都包含了所需的所有信息焕窝,當這些信息需要在查詢進行匹配時蹬挺,并不需要進行昂貴的關聯(lián)操作。
缺點:索引更新或刪除數(shù)據(jù)它掂,應用程序不得不處理寬表的冗余數(shù)據(jù)巴帮;由于冗余數(shù)據(jù),導致某些搜索和聚合操作可能無法按照預期工作虐秋。
嵌套文檔nested存儲
Nested類型是ES mapping定義的集合類型之一榕茧,它解決了原有object類型扁平化的字段屬性,導致查詢錯誤的問題客给,是支持獨立檢索的類型用押。
舉例:有一個文檔描述了一個帖子和一個包含帖子上所有評論的內(nèi)部對象評論“薪#可以借助于Nested實現(xiàn)蜻拨。
實踐注意1:當使用嵌套文檔時池充,使用通用的查詢方式是無法訪問到的,必須使用合適的查詢方式(nested query缎讼、nested filter纵菌、nested facet等),很多場景下休涤,使用嵌套文檔的復雜度在于索引階段對關聯(lián)關系的組織拼裝。
實踐注意2:
index.mapping.nested_fields.limit 缺省值為50
即:一個索引中最大允許擁有50個nested類型的數(shù)據(jù)笛辟。
index.mapping.mested_object.limit 缺省值是10000功氨。
即:1個文檔中所有nested類型json對象數(shù)據(jù)的總量是10000。
適用場景:對少量手幢,子文檔偶爾更新捷凄、查詢頻繁的場景。
如果需要索引對象數(shù)組并保持數(shù)組中每個對象的獨立性围来,則應使用嵌套Nested數(shù)據(jù)類型而不是對象Object數(shù)據(jù)類型跺涤。
優(yōu)點:nested文檔可以將父子關系的兩部分數(shù)據(jù)(舉例:博客+評論)關聯(lián)起來,可以基于nested類型做任何的查詢监透。
缺點:查詢相對慢桶错,更新子文檔需要更新整篇文檔。
父子文檔存儲
注意:6.X之前的版本的父子文檔存儲在相同索引的不同type中胀蛮。而6.X之上的版本院刁,單索引下已不存在多type的概念。父子文檔Join的都是基于相同索引相同type實現(xiàn)的粪狼。
Join類型是ES mapping定義的類型之一退腥,用于在同一索引的文檔中創(chuàng)建父/子關系。關系部分定義文檔中的一組可能關系再榄,每個關系是父名稱和子名稱狡刘。
適用場景:子文檔數(shù)據(jù)量要明顯多于父文檔的數(shù)據(jù)量,存在1對多的關系困鸥;子文檔更新頻繁的場景嗅蔬。
舉例:1個產(chǎn)品和供應商之間是1對N的關聯(lián)關系。
當使用父子文檔時窝革,使用has_child或has_parent做父子關聯(lián)查詢购城。
優(yōu)點:父子文檔可以獨立更新。
缺點:維護Join關系需要占據(jù)部分內(nèi)存虐译,查詢較Nested更耗資源瘪板。
小結
Nested Object和父子關系的這兩種的區(qū)別如下:
對比 | Nested Object | Parent/Child |
---|---|---|
優(yōu)點 | 文檔存儲在一起,因此讀取性能高 | 父子文檔可以獨立更新漆诽,互不影響 |
缺點 | 更新父或子文檔時需要更新整個文檔 | 為了維護Join的關系侮攀,需要占用部分內(nèi)存讀取性能較差 |
場景 | 子文檔偶爾更新锣枝,查詢頻繁 | 子文檔更新頻繁 |
注意1:在ES開發(fā)實戰(zhàn)中對于多表關聯(lián)的設計要突破關系型數(shù)據(jù)庫設計的思維定式。
注意2:不建議在ES做Join操作兰英,父子能實現(xiàn)部分功能撇叁,但是它的開銷比較大,如果有可能畦贸,盡量在設計時使用扁平的文檔模型陨闹。
注意3:盡量將業(yè)務轉化為沒有關聯(lián)關系的文檔形式,在文檔建模處多下功夫薄坏,以提升檢索效率趋厉。
注意4:Nested&Join父子類型,在選項時必須考慮性能問題胶坠。nested類型檢索使得檢索效率慢幾倍君账,父子Join類型檢索會使得檢索效率慢幾百倍。