了解EAV設計基本原理的最好方法就是理解行建模(row modelling,其中EAV是廣義形式)。 以一超市數據庫為例,必須管理數以千計的產品和品牌,其中許多產品存在期很短暫。那么,顯而易見,產品名稱不應該硬編碼為表的列名,反之,產品說明存儲在 產品表中,同一個產品的購買/銷售記錄,則分為多行存儲在另一表中,以產品ID與該表關聯。 EAV設計概念上涉及包含一個三列的表:實體(entity,如嗅覺受體的ID),屬性(attribute,如物種,實為指向元數據表的指針),屬性的 值(例如老鼠,rat)。 在EAV設計中,每行保存單一的數據(fact)。 傳統的表與此相反,每一屬性為一列,因此,一行存儲一組數據。 EAV方法適合符合該實體的參數數量遠大於具體某一實體所包含的參數數量時的情況。
許多開發人員在定義和分析數據需求時會遇到同樣的問題,就是許多不同的屬性可用來描述一個對象,但實際上只有很少的屬性適用於每個人。一種選擇是創建一具有每個屬性列的表,這種方法適用於具有固定數量屬性的對象,對大多數對象而言,其中全部或大部分屬性有值。 然而,在某些情況下,最終得到的記錄,其中大部分列將是空的,因為屬性可能是未知或不適用的。許多種類的數據必須表現出來,其數量可能出現波動。與此同時,即使不稀疏的屬性,然每類數據都非常少。這種情況下,傳統的數據模型將使成百的表卻只有幾行數據。
多種類的數據必須表現出來,其數量可能出現波動。與此同時,即使不稀疏的屬性,然每類數據都非常少。這種情況下,傳統的數據模型將使成百的表卻只有幾行數據。
為解決上述問題,可應用EAV(Entity,Attribute,Value)模型。這種模式也有幾個可選的稱謂,包括“對象-屬性-值(object—attribute-value)”模型和“開放架構(open schema)”幾種可供選擇的名稱。在EAV數據模型中,只有非空值才存儲在數據庫中,每個屬性-值(或鍵-值)對描述一個給定實體的屬性。EAV表經常定性為“長瘦(long and skinny)”的,“長”是指描述實體的多個行,而“瘦”則指僅使用少量的列。在本文中將描述EAV模型及其實現,並展示其在現實世界中的應用---包括研究Magento是如何應用這個模式。
對象屬性存儲在一個有三列的表中:實體,屬性和值(entity,attribute,value)。實體(entiry)表示所描述的數據項,例如一個產品或汽車。屬性(attribute)表示描述實體的數據,例如一個產品將有價格,重量和許多其他屬性。值(value)是屬性的值,例如產品可能有一個9.99英鎊的價格屬性。此外值可以基於數據類型進行分割,所以可將EAV表分為字符串、整數、日期和長文本(long text)表。依據數據類型分割是為了支持索引,使得數據庫執行可能的類型檢查驗證。
稀疏的屬性(Sparseness of Attributes)
在數學和計算機科學中,如果一個對象僅包含大量潛在屬性中的幾個屬性,稱之為“稀疏矩陣”。在討論EAV模型時,采用“稀疏”來描述大多數無值的屬性。
為說明這一點,來看看超市的收據,超市中有成千上萬的庫存產品,每日引入新的產品,其它則停止銷售。客戶購買5個產品的收據只列出實際購買的詳細項目,每行一種產品。該收據並沒有列出客戶可能購買的每一種產品,所以說客戶的收據是稀疏的。
按照數據庫的術語實體是銷售發票,包含如交易ID、日期和時間、存放位置等信息,收據中的每一行對應銷售表中的一行記錄,存有一個屬性及一個或多個值。在這種情況下,屬性之一是客戶所購買的產品,值則為的數量、單價、折扣和總價。
上面的例子說明了屬性的稀疏性(一個客戶只選購一個可能的產品),引入一個新的術語:行建模(row modelling)。基於行模型的表,其描述實體的數據記錄為多行,每組新的數據在數據庫中存為額外的行而非額外的列。行模型是數據庫設計時的標准數據建模技術,它僅適用於滿足如下兩個條件的情況:
- 特定實體的數據是稀疏的。
- 數據時易變的。
行建模是不適用於稀疏且數據非波動的情形,此時,應采用傳統的列模型。
行模型示例(Example of Row Modelling)
在這個例子中,有三個實體:產品,客戶和發票。產品和客戶是標准的關系表。
正如本文前面提到的,滿足(上文所述的)兩個條件時行模型需是一個不錯的選擇,即特定實體的數據稀疏,且很容易變化。
我們知道,新產品不斷推出且被引入銷售,而舊的產品撤回。同時,發票表中不能對每種產品以一列來描述,因為這是不切實際的。
該發票表包含有關銷售活動的主要信息,客戶、日期和時間、發票ID。每張發票,在表中記錄為一行。每一行指定了(顧客)購買的產品、單價和數量。
EAV與行模型比較(EAV vs Row Modelling)
實體-屬性-值的設計是行模型的泛化(或)推廣。這意味着整個數據庫所有類型的數據存儲在一張表中,其中一個行模型的表的數據時是均勻。此外,這行模型表中值列的數據類型是預先確定的,而在一EAV表中,特定行其值的數據類型由對應的屬性確定。
選擇數據模型的最佳方法是很難的,但作為一個准則,如滿足如下條件時請考慮EAV模型而非行模型:
- 數據記錄中的單個屬性的數據類型不同(是/否,數值型;Yes/No,numerical value,string);采用行模型時一張表中很難存儲屬性的值。
- 許多種類的數據必須表現出來,其數量可能出現波動。與此同時,即使不稀疏的屬性,然每類數據都非常少。這種情況下,傳統的數據模型將使成百的表卻只有幾行數據。
- 在一定的環境中,其類別/類必須在動態創建,某些類在原型隨后的周期中常常會被省略。
- 某些類歸類為混合型的類,這意味着一些類的屬性是稀疏的,而其他屬性則是非常稀少的。在這種情況下,非稀疏屬性存儲在傳統表中,而稀疏的屬性存儲在EAV或行建模的格式。這些類通常會滿足商業數據庫應用的需要,所有產品將共享這些屬性,如包裝單位和單價。請注意,如果只有一兩個混合類,EAV設計可能不值得。
實體、屬性和值表示(Representing Entities, Attributes and Values)
實體描述(Representing Entities)
一個實體可以是任何條目,到目前為止,所看到的例子是銷售事件實體、商人和產品實體。EAV管理的實體是通過一個對象表,用以獲取每個項目的共同數據,如名稱、說明等。對象表中的每一實體必須具有唯一的標識符,而這通常是自動生成的。然后,該標識符在整個數據庫作為外鍵。
采用EAV建模並不阻止使用傳統的數據表來存貯單個對象的更多細節信息。這是在同一數據庫模式(schema)內應用傳統關系數據庫模型和EAV模型的常見數據建模方法。
屬性描述(Representing Attributes)
屬性存儲在一個專門的屬性表。本表的主鍵是用來作為跨數據庫的參考。的屬性表通常輔以多個元數據表,更詳細地描述了一個屬性。此元數據信息通常用於自動化瀏覽和編輯數據的用戶界面生成。元數據表可能包含如下類型的信息部分:
- 驗證:驗證元數據包括屬性的數據類型、默認值、值數量的可能限制,以及可否為空(null)。
- 展現:定義屬性如何呈現給用戶,是否為文本區域、下拉框或單選按鈕/檢驗框(radio button/checkbox)組。
- 分組:屬性大多呈現在用戶群體。 分組中的元數據定義了一個屬性的顯示方式,提出了屬性的數量和什么樣的字體和顏色類型使用的順序。
- 正常值范圍:在某些情況下正常的值范圍,可能會隨性別、年齡有所不同,等等。
值描述(Representing Values)
描述EAV模型值的最簡單方法就是將其數據存儲為一個字符串。但是,這種方法是相對低效的,因為做任何事與該值相關的事情是都需要進行數據類型轉換。此外,對存儲為字符串的值創建的索引不允許針對數值型和日期型的搜索范圍優化,這是采用混合數據類型的鍵-值對描述數據的公共問題。
為改善這種情況,EAV模型為每一數據類型建立單獨的表。屬性元數據標識正確的數據類型以及隨后存儲數據的EAV表。這種方法更有效,因為它允許在訪問數據之前,緩存給定屬性的元數據。
這種方法的主要缺點在一個屬性的數據類型需要改變時是顯而易見的,需要將數據從一張表重新分配到另一表中,這很不方便,可以通過存儲過程來解決。
一般情況下,空值或不適用於此EAV模型。但某些情況下,存在記錄缺失值的必要原因。在這種情況下,解決方法是在表中添加缺失值的編碼列,僅在該列的值為空(null)時其值非空。然后,此編碼用於查找文字說明清單。
示例(Example)
EAV的最簡單實現可能只有三個表:實體,屬性和值。如此設置的示例如下所示:
然而,在這種實現中,缺失了元數據信息,不論何種數據類型,所有的值都存為varchar。作為對這一方法的衍生,可以另外選擇一個強類型的實現方法,其中一個給定數據類型的值存儲為特定類型表的數據記錄。下述模式(schema)例子所示,其中包括元數據信息,如前文所討論。
Magento采用EAV模型(Magento Uses The EAV Model)
在開源和php社區,最著名的EAV實現是Magento,一個電子商務平台。首先來看看Magento的數據庫模式(Magento database schema)。雖然一開始顯得復雜,我們逐步來瀏覽。
正如之前提到的,實體可以是任何條目或事件。Magento中包含多個實體,例如:客戶,訂單,發票和產品。出於本文的目的,將用產品實體來解釋EAV的實現。產品的主表為catalog_product_entity。但是,您可能會感到驚訝,只保存了幾類的信息,如實體類型,型號(SKU)以及產品創建時間。
為建立一個完整的產品記錄,需要找到它的屬性,然后找到每一屬性的值。在catalog_product_entity表中,會發現entity_type_id列,用來在整個數據庫中標識實體的類型。基於實體的類型,可以通過查找eav_attribute來找到要設置產品的那一屬性。此表記錄了Magento所有實體的全部屬性,也包含每一記錄的元信息,如數據類型、前端細節等;就產品而言,實體類型ID設置為4(類型在eav_entity_type表中列出),查詢欲設置的所有產品屬性,簡單操作如下:
1
|
SELECT
*
FROM
eav_attribute
WHERE
entity_type_id = 4;
|
屬性的名稱被記錄為attribute_code,元數據信息中,一個重要的列為backend_type,這表明一屬性為何種數據類型,該屬性的值存在何處。Magento的允許下列數據類型:
- static
- datetime
- decimal
- int
- text
- varchar
正如前面所述,值可以基於其數據類型存儲在多個表中。檢查一特定屬性,可以使用如下查詢:
1
|
SELECT
*
FROM
eav_attribute
WHERE
entity_type_id = 4
AND
attribute_code =
'name'
;
|
上述查詢運行后,可以看到,屬性“name”的數據類型為varchar,產品屬性的值跨多個表存儲:
catalog_product_entity_datetime, catalog_product_entity_decimal, catalog_product_entity_int, catalog_product_entity_text, catalog_product_entity_varchar。這些表說明了EAV模型中不同數據類型的存儲方式。
為獲得所有的產品,可使用的查詢列表如下:
1
2
3
4
5
6
7
|
SELECT
cpe.entity_id,value ASname
FROM
catalog_product_entity cpe
INNER
JOINeav_attribute ea
ON
cpe.entity_type_id =ea.entity_type_id
INNER
JOINcatalog_product_entity_varchar cpev
ON
ea.attribute_id =cpev.attribute_id
AND
cpe.entity_id = cpev.entity_id
WHERE
ea.attribute_code =
'name'
|
這個概念很簡單,一旦你知道從哪里開始以及如何查找表中的下一個層次。所有其他實體遵循同樣的原則:對於感興趣的對象先找到實體類型ID,然后基於該ID從eav_attribute中獲得所有屬性,最后,基於屬性的數據類型,從不同的表中查詢每一屬性的值。
EAV模型的優/缺點(Advantages and Disadvantages of the EAV Model)
EAV模型的主要優點是其靈活性。屬性描述表不限制列的數量,這意味着每次新增屬性不需要重新設計數據結構(schema);擴展數據庫時,屬性的數量可以垂直增加(每一新的參數在表中為一的記錄),而無需改變數據結構。
事實上,EAV只處理非空屬性意味着不需要為空值保留額外的存儲空間。這使得EAV模型相當節省空間。
物理數據格式是非常干凈,類似於XML,很容易將數據映射為XML格式,只需替換要開始和結束屬性標簽。
EAV模型可以極好地迅速擴展應用,因為它可以防止(屬性)不斷變化的后果。可以簡單地記錄任何結構的新數據,而不需要修改任何數據結構。
當考慮EAV時,確定數據是否稀疏和量大恆重要,因為采用不恰當的數據集時,EAV設計的復雜性超過了其優勢所在。相對靜態或簡單數據選用傳統的表結構更為合適。
相較於傳統的數據結構,EAV的一個主要缺點是它在檢索大容量數據時效率較低。在EAV模型中,數據更加分散,所以查詢(select)一個完整實體的記錄需要多個表連接。更重要的是,當EAV模型應用於大數據量時,對於同一組EAV建模的數據描述,需要短暫或永久地在列(column)和行之間進行轉換。該操作易於出錯且是CPU密集型的任務。
EAV模型的另一個局限性,需要制定額外的邏輯來完成傳統數據結構(/模式)下自動進行的述務。但是,利用現有的EAV工具可以降低此類工作的成本。
最后,理解EAV模型確實需要時間。它有一個明確的學習曲線,使的初級開發人員在真正理解其概念前,需要為此付出更多的精力。
結論(Conclusion)
應用實體-屬性-值時,應考慮以下條件:
- 數據是稀疏的、異構的,一個實體的屬性范圍較廣,且常引入新的屬性。
- 類的數量非常大,有許多實例類,即使屬性是非稀疏的。
- 有許多混合類,既具有稀疏也具有非稀疏屬性。通常情況下,並不是所有的數據類滿足EAV建模的要求。
在生產環境中,往往采用混合模式(mixed schema),包括傳統的關系、EAV或合適的混合方法。但是,EAV建模,需要引入元數據來獲取EAV的邏輯模型數據。我們看到Magento,受EAV影響很大,其中各種不同的產品將有很不同的屬性集,是一個運用該模型的非常有效的好例證。本文希望揭示什么是EAV模型,以及如何、何時應用它才更重要。如果您有更多應用EAV模型的例子,或有任何疑問,請作評論!
英文原文: http://techportal.ibuildings.com/2010/10/21/the-eav-data-model/