摘要
把繼承層次映射到關(guān)系型數(shù)據(jù)庫表有很多種做法。以下展示的模式是單種的映射方式,在實(shí)際中,你可以混合使用不同的映射方式。(2002-08-20 12:28:52)
By axing
繼承映射模式
把繼承層次映射到關(guān)系型數(shù)據(jù)庫表有很多種做法。以下展示的模式是單種的映射方式,在實(shí)際中,你可以混合使用不同的映射方式。
以下的討論沒有涉及多重繼承。在領(lǐng)域?qū)又泻苌贂?huì)遇到有多重繼承的情況。大多數(shù)多重繼承的使用都可以使用協(xié)議繼承來實(shí)現(xiàn),一個(gè)類從虛基類中繼承多種協(xié)議。協(xié)議類很少有需要持久性的,因此我們只討論簡單的繼承。
運(yùn)行實(shí)例
我們選用了一個(gè)稱為合伙人(partner)的系統(tǒng)中的一部分。一個(gè)群體(Party)由任何形式的人(person)組成,可以是自然人(natural person)或法人(institution)。客戶和員工都是群體。對(duì)于員工,我們將之區(qū)分為專職員工(SalariedEmployee)和兼職員工(FreelanceEmployee)。見下圖:
圖中使用的屬性比較少,現(xiàn)實(shí)中的系統(tǒng)中會(huì)有更多的屬性,但是我們這里只是列出了可以表示我們所討論的模式不同之處的屬性。因此它們之中都沒有涉及到讀咱的屬性或關(guān)系。并且我們假設(shè)以上層次中每一個(gè)類都不是虛基類,所有的五個(gè)類都可以生成實(shí)例。
模式: One Inheritance Tree One Table
摘要
該模式展示了一種方法,將一個(gè)完整的繼承層次映射到單個(gè)的數(shù)據(jù)庫表中。
問題
怎樣把一個(gè)完整的繼承層次映射到單個(gè)的數(shù)據(jù)庫表中?
約束
除了文章一開始列出的通用約束,還包括了如下的約束:
- 多態(tài)讀取、存儲(chǔ)空間和寫/更新性能的對(duì)比:在一個(gè)繼承層次中,你需要支持這樣的查詢:在給定的條件下,查找所有匹配的Party對(duì)象。結(jié)果集還必須是多態(tài)的。在該例中,包括Employee、FreelanceEmployee或SalariedEmployee。這種方案雖然能夠支持多態(tài)查詢,但是既浪費(fèi)空間,也影響了寫/更新性能。
- 數(shù)據(jù)庫的鎖模式:有些數(shù)據(jù)庫只是實(shí)現(xiàn)了頁面鎖,這樣的話,你就需要注意數(shù)據(jù)庫的流量,不要讓一個(gè)表中的鎖超出了限制的數(shù)量而影響性能。如果把過多的類映射到單個(gè)表中,很明顯,你必須注意流量的問題。
- 繼承層次的深度:某些方案適合于處理扁平性的繼承層次,但難于處理縱深的繼承層次。
- 維護(hù)負(fù)擔(dān):把單個(gè)的對(duì)象的數(shù)據(jù)跨越多個(gè)表來存放,這樣的做法可以加快多態(tài)讀取的速度,但缺點(diǎn)是增加了維護(hù)量,一旦對(duì)象需要新增或刪除屬性,模式的發(fā)展也需要考慮物理數(shù)據(jù)模型中數(shù)據(jù)的重復(fù)問題,這極易導(dǎo)致維護(hù)的噩夢(mèng)。其它的維護(hù)實(shí)例還包括在一個(gè)繼承層次中插入類和刪除類。
- 用戶自定義的查詢:如果你希望給用戶賦予自定義查詢的權(quán)利,那么你需要從用戶的角度考慮該映射是否能為用戶所理解。
解決方案
把繼承層次中的所有對(duì)象的屬性全集對(duì)應(yīng)到數(shù)據(jù)庫的單個(gè)表中。每條記錄中那些無用的字段使用Null值。
結(jié)構(gòu)
解決方案示例
上例中的表設(shè)計(jì)如下圖:
結(jié)論
- 寫/更新性能:One Inheritance Tree One Table允許在單詞的數(shù)據(jù)庫操作中對(duì)任何基類的任何一個(gè)子類讀取和寫入。
- 多態(tài)讀性能:象全部的基類的子類都可以在一張表中找到一樣,多態(tài)讀也很簡單。唯一的難點(diǎn)就是為一條選中的記錄構(gòu)造正確的對(duì)象類型。處理這種情況的模式有很多,例如Abstract Interface[Col96]。
- 所占空間:就像你在上面的映射中所描述的,存儲(chǔ)對(duì)象的屬性會(huì)浪費(fèi)一些空間,浪費(fèi)空間的多少取決于繼承層次的深度。層次越深,不同的屬性越多,屬性的全集就越大,也就越浪費(fèi)空間。
- 通過多個(gè)表來均衡數(shù)據(jù)庫負(fù)載:將過多的類映射到單個(gè)表中會(huì)導(dǎo)致低性能。可以在如下的數(shù)據(jù)庫行為中發(fā)現(xiàn)這些問題的來源:
- 如果數(shù)據(jù)庫使用頁面級(jí)鎖,一張表上的過量的傳輸量會(huì)大大降低數(shù)據(jù)庫的訪問速度。巧妙的安排簇可以抵消部分的開銷。如果單個(gè)表上的傳輸量超過限制,性能將繼續(xù)惡化,甚至引起死鎖。
- 在單個(gè)表上太多的鎖還會(huì)導(dǎo)致鎖擴(kuò)散。鎖的數(shù)量是關(guān)系型數(shù)據(jù)庫系統(tǒng)中鎖擴(kuò)散問題的罪魁禍?zhǔn)住?
- 某些類需要次級(jí)索引來提高搜索速度。如果你在一個(gè)單獨(dú)的數(shù)據(jù)庫表中實(shí)現(xiàn)了很多類,相應(yīng)的你也需要在表中加入索引。一張表上過多的索引會(huì)使得更新的時(shí)候需要同時(shí)更新所有的索引,降低了速度。
- 維護(hù)成本:當(dāng)映射比較直接、比較簡單的時(shí)候,只要繼承的層次不會(huì)很多,模式的改進(jìn)相對(duì)也會(huì)比較直接和簡單。
- 特殊查詢:由于映射和直觀,因此一些特殊的查詢也會(huì)相對(duì)較容易。
實(shí)現(xiàn)
- 考慮將所有的對(duì)象都映射到單個(gè)的表:你可以把所有的對(duì)象類型都放在單個(gè)表中--這將導(dǎo)致表上的大傳輸量。對(duì)于小型的應(yīng)用系統(tǒng)類說,這不失為切實(shí)可行且靈活的方法。
- 空間的浪費(fèi):你需要檢查關(guān)系數(shù)據(jù)庫是否允許空值壓縮。如果允許的話,那么這種映射方法無疑將更具吸引力,因?yàn)榭罩狄膊粫?huì)浪費(fèi)空間。類型標(biāo)識(shí):表中需要插入類型信息。可以在Synthetic Object Identities中包含類型信息。
相關(guān)模式
參看Representing Inheritance in a Relational Database [Bro+96]
模式:One Class One Table
摘要
該模式討論了如何把一個(gè)繼承層次中各個(gè)類映射到不同的數(shù)據(jù)庫表中。
問題
如何把繼承層次中的類映射到數(shù)據(jù)庫表中?
約束
參見One Inheritance Tree One Table模式。
解決方案
將每個(gè)類的屬性放到不同的表中。在每個(gè)表中插入一個(gè)Synthetic OID,將子類的行和父類所在表的行關(guān)聯(lián)起來。
結(jié)構(gòu)
解決方案示例
將前例中的類映射到數(shù)據(jù)庫后產(chǎn)生五張表--每個(gè)類一張。單個(gè)的SalariedEmployee將存儲(chǔ)在五張表其中的三張中:
結(jié)論
- 寫/更新性能:該模式提供了非常靈活的映射機(jī)制,但缺乏一個(gè)較好的性能。考慮在上例中讀取FreelanceEmployee的一個(gè)實(shí)例。它將會(huì)包括三次的數(shù)據(jù)庫讀操作:一次讀FreelanceEmployee表, 一次讀Employee表,一次讀Party表。寫同樣需要三次操作,并更新一個(gè)或多個(gè)索引。在讀寫相關(guān)的任務(wù)上,這種映射方式相當(dāng)費(fèi)資源,這個(gè)代價(jià)還隨著繼承層次的增多而增大。
- 多態(tài)讀性能:在我們的例子中,F(xiàn)reelanceEmployee的實(shí)例分別對(duì)應(yīng)于不同表的Employee實(shí)例和Party實(shí)例。因此,多態(tài)讀只需要讀取一張表。這是該模式除去節(jié)約空間的優(yōu)點(diǎn)之外吸引人的另一大優(yōu)點(diǎn)。
- 所占空間:這種映射幾乎有最佳的空間節(jié)約性。唯一的容易就是額外的synthetic OID,來連接不同的層級(jí)層次。
- 維護(hù)成本:由于這種映射比較直接,也易于了解,模式的改進(jìn)也相應(yīng)較為直接、容易。
- 特殊查詢:由于映射需要訪問多個(gè)表來獲取一個(gè)對(duì)象實(shí)例的數(shù)據(jù),特殊的查詢比較難以組織,尤其是對(duì)于無經(jīng)驗(yàn)的用戶。
- 根表上的重負(fù)載:該模式將會(huì)導(dǎo)致根對(duì)象類型表上的重負(fù)載。在該例中,持有FreelanceEmployee表的寫入鎖的事務(wù)將同時(shí)持有對(duì)Party表和Employee表的寫入鎖。參看One Inheritance Tree One Table的結(jié)論部分對(duì)數(shù)據(jù)庫表瓶頸的負(fù)面影響的討論。
實(shí)現(xiàn)
- 虛類:注意虛類也需要映射到單獨(dú)的表中。
- 類型標(biāo)識(shí):從前一個(gè)模式的例子中,我們假設(shè)Synthetic Object Identity已經(jīng)包含了類型信息。為了從多態(tài)讀取查詢返回的結(jié)果中構(gòu)建正確的類型,需要了解某些類型信息。
相關(guān)模式
參看Representing Inheritance in a Relational Database [Bro+96]。
模式:One Inheritance Path One Table
摘要
該模式顯式了一種將繼承路徑中的所有屬性放到單個(gè)的數(shù)據(jù)庫表中的方法。
問題
如何把一個(gè)繼承層次中的所有類映射到數(shù)據(jù)庫的表中?
約束
和One Inheritance Tree One Table模式的約束相同。
解決方案
將每個(gè)類的屬性映射到不同的表中。并在表中加入該類的父類的所有屬性。
結(jié)構(gòu)
解決方案示例
上例中映射的結(jié)果將會(huì)產(chǎn)生五張表--每個(gè)類一張。SalariedEmployee用其中的一張表就可以表示,SalariedEmployee的映射方法如下:
結(jié)論
- 寫/更新性能:映射需要單次的數(shù)據(jù)庫操作來完成對(duì)一個(gè)對(duì)象的讀寫。
- 多態(tài)讀性能:本例中,對(duì)所有的Party對(duì)象的查詢意味著要訪問五張表。和One Class One Table模式移機(jī)One Inheritance Tree One Table模式比起來是不小的開銷。
- 所占空間:這種映射提供了最佳的空間占用。沒有任何的冗余屬性,甚至連額外的synthetic OID都沒有。
- 維護(hù)成本:產(chǎn)物一個(gè)新的子類意味著需要更新所有的多態(tài)查詢。增加或刪除父類的屬性將會(huì)導(dǎo)致對(duì)所有子類的表結(jié)構(gòu)的修改。
- 特殊查詢:由于映射需要訪問不只一張表來完成多態(tài)搜索,特殊的多態(tài)查詢對(duì)無經(jīng)驗(yàn)的用戶來說難以編寫。對(duì)葉結(jié)點(diǎn)類的查詢將會(huì)非常的復(fù)雜。
實(shí)現(xiàn)
- 虛類:注意虛類不要映射到表。
- 類型標(biāo)識(shí):不需要在表中插入類型信息,因?yàn)榭梢酝ㄟ^表的名稱來區(qū)分對(duì)象類型。因?yàn)镾ynthetic Object Identities包含了類型信息,可以考慮減少它的長度來節(jié)省部分的空間。
相關(guān)模式
參見Representing Inheritance in a Relational Database [Bro+96]。
模式:Objects in BLOBs
摘要
該模式展示了一種使用BLOB,將對(duì)象映射到單個(gè)數(shù)據(jù)庫表的方法。該模式覆蓋了繼承、聚合和關(guān)聯(lián)。
問題
怎樣把對(duì)象映射到關(guān)系型數(shù)據(jù)庫?
約束
參見One Inheritance Tree One Table模式。
解決方案
表包括兩個(gè)字段:一個(gè)是synthetic OID,另一個(gè)是變長的BLOB,后者囊括了一個(gè)對(duì)象內(nèi)的所有數(shù)據(jù)。使用流把對(duì)象的數(shù)據(jù)存放到BLOB中。
結(jié)構(gòu)
解決方案示例
任何一種的設(shè)計(jì)方案都和上圖相似。
結(jié)論
- 寫/更新性能:Objects in BLOBs可以在單次的數(shù)據(jù)庫操作中讀取、寫入基類的任何子類。注意,在很多RDBMS中,BLOBs都不是最快的訪問數(shù)據(jù)的類型。
- 多態(tài)讀:搜索類來獲得屬性是非常困難的。由于你沒有辦法訪問BLOB的內(nèi)部結(jié)構(gòu),因此就需要在數(shù)據(jù)庫中注冊(cè)函數(shù),以便于訪問屬性。這種函數(shù)的實(shí)現(xiàn)可以參考[Loh+91]。定義和維護(hù)這些函數(shù)也需要付出客觀的代價(jià)。
- 特殊查詢:由于搜索類中的屬性難以實(shí)現(xiàn),因此特殊查詢也同樣難于表達(dá)。還必須要額外的定義函數(shù)。
- 所占空間:如果你的數(shù)據(jù)庫可以實(shí)現(xiàn)變長的BLOBs,所占空間就是最小的。
- 維護(hù)成本:數(shù)據(jù)庫模式的演進(jìn)將會(huì)和面向?qū)ο髷?shù)據(jù)庫一樣容易。
實(shí)現(xiàn)
- 相似實(shí)現(xiàn)的起源:Objects in BLOBs已用于原型的研究,該原型正盡可能的像OODBMS靠近,只不過它是使用一個(gè)關(guān)系型數(shù)據(jù)庫作為存儲(chǔ)管理。因此該模式的實(shí)現(xiàn)機(jī)會(huì)等于是在一個(gè)已存在的存儲(chǔ)管理器的基礎(chǔ)上實(shí)現(xiàn)OODBMS。
- 表間的數(shù)據(jù)庫負(fù)載均衡:把過多的類映射到一個(gè)單獨(dú)的表會(huì)造成低性能。具體的討論可以參見One Inheritance Tree One Table模式的相關(guān)章節(jié)。
- 把OODB的特點(diǎn)和關(guān)系型數(shù)據(jù)庫相結(jié)合:把這個(gè)模式同其它類的對(duì)象/關(guān)系映射相結(jié)合是切實(shí)可行的。在這個(gè)例子中,BLOB存儲(chǔ)了復(fù)雜的對(duì)象的結(jié)構(gòu),類似于一個(gè)項(xiàng)目計(jì)劃圖一樣。可以設(shè)置其它的字段來存儲(chǔ)信息,以供那些訪問已結(jié)構(gòu)化的數(shù)據(jù)的特殊查詢使用。(見圖3,這種用BLOBs來存儲(chǔ)對(duì)象的方式已用于SMRC原型研究[Rei+94, Rei+96]。 SMRC中的BLOBs不僅存儲(chǔ)單個(gè)類的屬性,還包含了一個(gè)對(duì)象網(wǎng),這個(gè)對(duì)象網(wǎng)采用流方式存儲(chǔ)在BLOBs中。流方式存儲(chǔ)在圖3中描述)。此外,除了我們上面介紹的純粹的使用這種方法外,它還允許關(guān)系型數(shù)據(jù)和OODBMS共存。
圖3:對(duì)象數(shù)據(jù)和關(guān)系型數(shù)據(jù)的共存。
相關(guān)模式
當(dāng)只使用該模式的時(shí)候,這個(gè)模式類似于One Inheritance Tree One Table映射,參看Representing Inheritance in a Relational Database [Bro+96]。