《图数据库(第2版)》——2.1 关系型数据库缺少联系
本節(jié)書摘來自異步社區(qū)出版社《圖數(shù)據(jù)庫(第2版)》一書中的第2章,第2.1節(jié),作者:【美】Ian Robinson(伊恩?羅賓遜) , Jim Webber(吉姆?韋伯) , Emil Eifrem(埃米爾?艾弗雷姆),更多章節(jié)內(nèi)容可以訪問云棲社區(qū)“異步社區(qū)”公眾號查看。
2.1 關(guān)系型數(shù)據(jù)庫缺少聯(lián)系
數(shù)十年來,開發(fā)者試圖使用關(guān)系型數(shù)據(jù)庫處理關(guān)聯(lián)的、半結(jié)構(gòu)化的數(shù)據(jù)集。關(guān)系型數(shù)據(jù)庫設(shè)計之初是為了處理紙質(zhì)表格以及表格化結(jié)構(gòu)—有些方面關(guān)系型數(shù)據(jù)庫做得非常好—它們試圖對這種實際中的特殊聯(lián)系進(jìn)行建模。然而諷刺的是,關(guān)系型數(shù)據(jù)庫在處理聯(lián)系上做得卻并不好。
聯(lián)系確實存在于關(guān)系型數(shù)據(jù)庫自身的術(shù)語中,但只出現(xiàn)在建模階段,作為連接表的手段。前面章節(jié)在對關(guān)聯(lián)數(shù)據(jù)的討論中曾經(jīng)提到,我們經(jīng)常需要對連接實體的聯(lián)系進(jìn)行語義的區(qū)分,并為其定義權(quán)重。關(guān)聯(lián)關(guān)系什么也做不了。更糟糕的是,隨著離群數(shù)據(jù)(outlier data)的成倍地增加,數(shù)據(jù)集的宏觀結(jié)構(gòu)會越發(fā)復(fù)雜和不規(guī)整,關(guān)系模型將造成大量表連接、稀疏行和非空檢查邏輯。關(guān)系世界中連通性的增強(qiáng)都將轉(zhuǎn)化為連接操作的增加,這會阻礙性能,并使已有的數(shù)據(jù)庫難以響應(yīng)變化的業(yè)務(wù)需求。
圖2-1展示了一個以客戶為中心的、事務(wù)型應(yīng)用程序存儲顧客訂單的關(guān)系模式(relational schema)。
這種模式的設(shè)計對該應(yīng)用程序產(chǎn)生了很大影響,使有些查詢非常簡單,而有些異常困難。
表連接增加了偶發(fā)復(fù)雜性,使得業(yè)務(wù)數(shù)據(jù)與外鍵元數(shù)據(jù)混雜起來。
外鍵約束增加了額外的開發(fā)與維護(hù)成本,而目的僅僅是為了讓數(shù)據(jù)庫工作。
帶有空值列的稀疏表在代碼中需要額外檢查,盡管模式本身提供了相關(guān)支持。
僅因為想要查看客戶買了什么就需要好幾個昂貴的連接。
反向查詢(reciprocal query)代價更高。查詢“某個客戶買了哪些商品?”比查詢“有哪些客戶買了某個商品?”的代價相對要低,而這是推薦系統(tǒng)的基礎(chǔ)。對于這樣的需求,可以引入索引,然而即使引入索引,隨著遞歸程度的增加,諸如“有哪些買這個商品的客戶也買了那個商品?”這樣的遞歸問題的查詢代價變得異常高昂。
關(guān)系型數(shù)據(jù)庫在強(qiáng)關(guān)聯(lián)領(lǐng)域表現(xiàn)也很掙扎。讓我們通過社交網(wǎng)絡(luò)領(lǐng)域中的一些或是簡單或是不那么簡單的查詢來理解關(guān)系型數(shù)據(jù)庫中關(guān)聯(lián)查詢的成本吧。
圖2-2展示了一個記錄朋友關(guān)系的簡單的連接表設(shè)計。
回答“誰是Bob的朋友?”這個問題很簡單,如示例2-1所示。
示例2-1 Bob的朋友
SELECT p1.Person FROM Person p1 JOIN PersonFriendON PersonFriend.FriendID = p1.ID JOIN Person p2ON PersonFriend.PersonID = p2.ID WHERE p2.Person = 'Bob'基于這個示例數(shù)據(jù),答案是Alice和Zach。這不是一個代價高昂或困難的查詢,因為它使用了WHERE Person.person='Bob',使得返回的行數(shù)是可預(yù)期的和有限的。
朋友關(guān)系不總是自反關(guān)系(reflexive relationship),因此在示例2-2中,需要回答示例2-1的反向查詢:“Bob是誰的朋友?”
示例2-2 Bob是誰的朋友
SELECT p1.Person FROM Person p1 JOIN PersonFriendON PersonFriend.PersonID = p1.ID JOIN Person p2ON PersonFriend.FriendID = p2.ID WHERE p2.Person = 'Bob'這個查詢的答案是Alice,很遺憾Zach不認(rèn)為Bob是他的朋友。這個反向查詢在實現(xiàn)上也很簡單,但是數(shù)據(jù)庫端的花銷就略微大些了,因為所有的數(shù)據(jù)行都在表PersonFreiend中。
我們可以加入索引,然而仍會涉及代價高昂的中間層。當(dāng)問題是“誰是我的朋友的朋友?”時就更麻煩了。SQL的層級結(jié)構(gòu)使用了遞歸連接,使得查詢語法和計算都更加復(fù)雜,如示例2-3所示。(有些關(guān)系型數(shù)據(jù)庫提供這種查詢的語法糖。例如,Oracle提供了函數(shù)CONNECT BY,它可以簡化查詢語句,但并不能降低底層的計算復(fù)雜度。)
示例2-3 Alice的朋友的朋友們
SELECT p1.Person AS PERSON, p2.Person AS FRIEND_OF_FRIEND FROM PersonFriend pf1 JOIN Person p1ON pf1.PersonID = p1.ID JOIN PersonFriend pf2ON pf2.PersonID = pf1.FriendID JOIN Person p2ON pf2.FriendID = p2.ID WHERE p1.Person = 'Alice' AND pf2.FriendID <> p1.ID即使只是處理Alice的朋友的朋友們,而非深入Alice的整個社交網(wǎng)絡(luò),這個查詢計算也很復(fù)雜。當(dāng)我們試圖探究社交網(wǎng)絡(luò)時,問題則更復(fù)雜,代價也更高。雖然“誰是我的朋友的朋友們?”這樣的問題還是可能在合理的時間內(nèi)通過查詢得到答案,但是當(dāng)查詢延伸到第4度、第5度或第6度的朋友關(guān)系時,由于遞歸的連表查詢使此時的時間空間復(fù)雜度非常高,從而使查詢的效率嚴(yán)重惡化。
無論試圖在關(guān)系型數(shù)據(jù)庫中對關(guān)聯(lián)建模還是查詢,結(jié)果都與我們的愿望完全不同。除了之前指出的查詢和計算復(fù)雜度以外,我們還得去處理模式(schema)這個雙刃劍。很多時候,模式被證明太死板、太脆弱。死板表現(xiàn)在:我們需要在創(chuàng)建稀疏表的同時在代碼中處理異常情況—一切只是因為沒有一個能夠容納我們所用到的各類數(shù)據(jù)的通用的模式。這在增加了耦合度的同時也摧毀了其貌似存在的凝聚力。脆弱表現(xiàn)在:當(dāng)應(yīng)用程序發(fā)生變化時,需要增加額外的工作從一個模式遷移到另一個模式。
總結(jié)
以上是生活随笔為你收集整理的《图数据库(第2版)》——2.1 关系型数据库缺少联系的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WanaCrypt0r勒索蠕虫完全分析报
- 下一篇: 《树莓派实战秘籍》——2.7 技巧27尝