大數(shù)據(jù)時代和Hadoop時代的維度建模和Kimball數(shù)據(jù)集市
維度建模已死?
在回答這個問題之前,讓我們回頭來看看什么是所謂的維度數(shù)據(jù)建模。
為什么需要為數(shù)據(jù)建模?
有一個常見的誤區(qū),數(shù)據(jù)建模的目的是用 ER 圖來設(shè)計物理數(shù)據(jù)庫,實際上遠(yuǎn)不僅如此。數(shù)據(jù)建模代表了企業(yè)業(yè)務(wù)流程的復(fù)雜度,記錄了重要的業(yè)務(wù)規(guī)則和概念,并有助于規(guī)范企業(yè)的關(guān)鍵術(shù)語。它清晰地闡述、協(xié)助企業(yè)揭示商業(yè)過程中模糊的想法和歧義。此外,可以使用數(shù)據(jù)模型與其他利益相關(guān)者進行有效溝通。沒有藍圖,不可能建造一個房子或橋梁。所以,沒有數(shù)據(jù)模型這樣一個藍圖,為什么要建立一個數(shù)據(jù)應(yīng)用,比如數(shù)據(jù)倉庫呢?
為什么需要維度建模?
維度建模是數(shù)據(jù)建模的一種特殊方法。維度建模有兩個同義詞,數(shù)據(jù)集市和星型結(jié)構(gòu)。星型結(jié)構(gòu)是為了更好地進行數(shù)據(jù)分析,參考下面圖示的維度模型,可以有一個很直觀的理解。通過它可以立即知道如何通過客戶、產(chǎn)品、時間對訂單進行分割,如何通過度量的聚集和比較對訂單業(yè)務(wù)過程進行績效評估。
維度建模最關(guān)鍵的一點,是要定義事務(wù)性業(yè)務(wù)過程中的***粒度是什么。如果切割或鉆入數(shù)據(jù),到葉級就不能再往下鉆取。從另一個角度看,星型結(jié)構(gòu)中的***粒度,即事實和維度之間沒有進行任何聚集的關(guān)聯(lián)。
數(shù)據(jù)建模和維度建模
標(biāo)準(zhǔn)數(shù)據(jù)建模的任務(wù),是消除重復(fù)和冗余的數(shù)據(jù)。當(dāng)數(shù)據(jù)發(fā)生變化時,我們只需在一個地方修改它,這有助于保證數(shù)據(jù)的質(zhì)量,避免了不同地方的數(shù)據(jù)不同步。參考下面圖示的模型,它包含了代表地理概念的幾張表。在規(guī)范化模型中,每個實體有一個獨立的表,數(shù)據(jù)建模只有一張表:geography。在這張表中,city 會重復(fù)出現(xiàn)很多次。而對于每個 city,如果 country 改變了名字,就不得不在很多地方進行更新。
注:標(biāo)準(zhǔn)數(shù)據(jù)模型總是遵守 3NF 模式。
標(biāo)準(zhǔn)的數(shù)據(jù)建模,本身并不是為了商業(yè)智能的工作負(fù)載而設(shè)計的。太多的表會導(dǎo)致過多的關(guān)聯(lián),而表關(guān)聯(lián)會導(dǎo)致性能下降,在數(shù)據(jù)分析中我們要盡力去避免這種情形發(fā)生。數(shù)據(jù)建模過程中,通過反規(guī)范化把多個相關(guān)表合并成一個表,例如前面例子里的多個表被預(yù)合并成一個 geography 表。
那么為何部分人認(rèn)為維度建模已死?
一般人都認(rèn)可數(shù)據(jù)建模的方式,而把維度建模當(dāng)成特殊處理方式,它們都是有價值的。那為什么在大數(shù)據(jù)和 Hadoop 的時代,部分人會認(rèn)為維度建模沒用了?
“數(shù)據(jù)倉庫之死”
首先,一些人混淆了維度建模和數(shù)據(jù)倉庫。他們認(rèn)為數(shù)據(jù)倉庫已死,于是得出結(jié)論:維度建模也可以被丟進歷史的垃圾箱。這種論點在邏輯上是連貫的,但是,數(shù)據(jù)倉庫的概念遠(yuǎn)沒有過時。我們總是需要集成的、可靠的數(shù)據(jù)來產(chǎn)生商業(yè)智能儀表盤(BI Dashboards)。
只讀結(jié)構(gòu)的誤解
第二個常聽見的爭論,比如“我們遵循只讀方式的結(jié)構(gòu)(Schema),所以不需要對數(shù)據(jù)再進行建模了”。依我看來,這是數(shù)據(jù)分析過程中***的誤解之一。我同意起初僅轉(zhuǎn)儲原始數(shù)據(jù),這時不過多考慮結(jié)構(gòu)是有意義的。但是,這不應(yīng)該成為不對數(shù)據(jù)進行建模的借口。只讀方式的結(jié)構(gòu)只是降低了下游系統(tǒng)的能力和責(zé)任,一些人不得不咬牙去定義數(shù)據(jù)類型。訪問無模式數(shù)據(jù)轉(zhuǎn)儲的每一個進程都需要自己弄清楚發(fā)生了什么,而這完全是多余的。通過定義數(shù)據(jù)類型和正確的結(jié)構(gòu),可以很容易地避免這些工作。
再談反規(guī)范化和物理模型
是否那些宣傳維度建模的觀點實際上已過時了?的確有些觀點比上面列出的兩條更好,要理解它們需要對物理建模和 Hadoop 的工作方式有一些了解。
前面簡單提到采用維度建模的原因之一,和數(shù)據(jù)的物理存儲方式有關(guān)。標(biāo)準(zhǔn)數(shù)據(jù)建模中每個真實世界里的實體,有一個自己的表。我們這樣做,是為了避免數(shù)據(jù)冗余和質(zhì)量問題在數(shù)據(jù)中蔓延。越多的表,就需要越多的關(guān)聯(lián),這是標(biāo)準(zhǔn)建模的缺點。表關(guān)聯(lián)的代價是昂貴的,特別是關(guān)聯(lián)數(shù)據(jù)集中關(guān)聯(lián)大量記錄的時候尤其突出。當(dāng)我們考慮維度建模時,會把多個表合并起來,這就是所謂的預(yù)關(guān)聯(lián)或者說數(shù)據(jù)反規(guī)范化。***的結(jié)果是,得到更少的表、更少的關(guān)聯(lián)、更低的延遲和更好的查詢性能。
可參與領(lǐng)英上相關(guān)的討論。
徹底反規(guī)范化
為什么不把反規(guī)范化做到徹底?去掉所有的表關(guān)聯(lián)只保留一張表?的確,這樣做可以不需要對任何表進行關(guān)聯(lián),但是可以想象到,它會帶來一些負(fù)面影響。首先,它需要更多的存儲,因為要存儲大量的冗余數(shù)據(jù)。隨著數(shù)據(jù)分析的列式存儲格式的出現(xiàn),這一點現(xiàn)在不那么令人擔(dān)憂了。反規(guī)范化***的問題是,每次屬性值發(fā)生變化,就不得不在很多地方進行更新,可能是幾千甚至幾百萬次更新。一個解決辦法是在晚上對模型進行全量重載,通常這比增量更新要更快、更容易。列式數(shù)據(jù)庫通常采用這種方法,首先將要做的更新存儲在內(nèi)存中,然后異步地寫入磁盤。
分布式關(guān)系型數(shù)據(jù)庫(MPP)上的數(shù)據(jù)分布
在 Hadoop,例如 Hive、SparkSQL 上建立維度模型,要很好地理解一個技術(shù)上的核心特征,就是它和分布式關(guān)系型數(shù)據(jù)庫(MPP)上的建立方式是不一樣的。在 MPP 中的節(jié)點上分布數(shù)據(jù),可以控制每條數(shù)據(jù)記錄的位置?;诜謪^(qū)策略,例如 Hash、List、Range 等,可以在同一個節(jié)點上跨表同定位(co-located)各個記錄的鍵值。由于數(shù)據(jù)的局部性得到保證,關(guān)聯(lián)速度會非??欤驗椴恍枰诰W(wǎng)絡(luò)上發(fā)送任何數(shù)據(jù)。參考下面圖示的例子,在 ORDER 和 ORDER_ITEM 表中有相同 ORDER_ID 的記錄存儲在同一節(jié)點上:
ORDER 和 ORDER_ITEM 表中 ORDER_ID 對應(yīng)的鍵值,在相同的節(jié)點做到同定位。
Hadoop上的數(shù)據(jù)分布
數(shù)據(jù)分布在基于 Hadoop 的系統(tǒng)中是非常不同的,我們將數(shù)據(jù)分割成大型的塊(chunks),并在 Hadoop 分布式文件系統(tǒng)(HDFS)的各個節(jié)點進行分發(fā)和復(fù)制。這種數(shù)據(jù)分發(fā)策略不能保證數(shù)據(jù)的一致性。參考下面圖示的例子,記錄 ORDER_ID 的鍵被存儲在不同的節(jié)點:
為了關(guān)聯(lián)它們,需要在網(wǎng)絡(luò)上發(fā)送數(shù)據(jù),這樣做會影響性能。
處理這個問題的一個策略,是在集群的所有節(jié)點上復(fù)制要關(guān)聯(lián)的表,該策略被稱為廣播式關(guān)聯(lián)(broadcast join)。如果對 MPP 使用相同的策略,可以想象,只能用在較小的 lookup 或維度表中。
那么當(dāng)關(guān)聯(lián)一個大的事實表和一個大的維度表,比如客戶或產(chǎn)品,甚至關(guān)聯(lián)兩個大型事實表時,我們該怎么辦?
Hadoop上的維度建模
為了解決性能問題,可以利用反規(guī)范化將大的維度表放進事實表,以保證數(shù)據(jù)是同定位的(co-located),而對較小的維度表可以在所有節(jié)點上廣播(broadcast)。
關(guān)聯(lián)兩個大型事實表時,可以把低粒度的表嵌套到更高粒度的表中,例如把 ORDER_ITEM 表嵌套到 ORDER 表中。高級的查詢引擎,比如 Impala 或 Drill 可以讓數(shù)據(jù)扁平化(flatten out):
嵌套數(shù)據(jù)的策略很有用,類似于 Kimball 概念中用橋接表來表示維度模型中的 M:N 關(guān)系。
Hadoop和緩慢變化維
Hadoop 文件系統(tǒng)中的存儲是不可變的,換句話說,只能插入和追加記錄,不能修改數(shù)據(jù)。如果你熟悉的是關(guān)系型數(shù)據(jù)倉庫,這看起來可能有點奇怪。但是從內(nèi)部機制看,數(shù)據(jù)庫是以類似的機制工作,在一個進程異步地更新數(shù)據(jù)文件中的數(shù)據(jù)之前,將所有變更保存在一個不可變的預(yù)寫式日志(WAL- write-ahead log,Oracle中稱為redo log)中。
不可變性(immutability)對維度模型有什么影響?你也許還記得維度建模課程中漸變維的概念(Slowly Changing Dimensions – SCDS)。SCDS 有選擇地保存屬性值變更的歷史,于是可以在某個時間點上對屬性值進行度量。但這不是默認(rèn)的處理方式,默認(rèn)情況下會用***的值來更新維度表。那么在 Hadoop 上如何選擇呢?記住!我們不能更新數(shù)據(jù)。我們可以簡單地為 SCD 選擇默認(rèn)方式并對每一個變化進行審核(audit)。如果想運行基于當(dāng)前值的報表,可以在 SCD 之上創(chuàng)建一個視圖,讓它僅僅檢索到***值,利用 Windows 函數(shù)可以很容易做到這一點。或者,可以運行一個所謂合并(Compaction)的服務(wù),用***的值物理地創(chuàng)建維度表的一個單獨版本。
Hadoop的存儲演化
Hadoop 平臺的供應(yīng)商并沒有忽視這些 Hadoop 的限制,例如 Hive 就提供了滿足 ACID 的事務(wù)和可更新的表。根據(jù)大量的主要公開問題以及個人經(jīng)驗,這個特性還沒有完善到可以部署生產(chǎn)環(huán)境。Cloudera 采取了另外一個手段,利用 Kudu 建立了一個新的可變更存儲格式,它并沒有基于 HDFS,而是基于本地 OS 操作系統(tǒng)。它完全擺脫了 Hadoop 的限制,類似于列式 MPP 的傳統(tǒng)存儲層。通常來說,在 Impala + Kudu 這樣一個 MPP 上運行 BI 和 Dashboard 的任何使用場景,會比 Hadoop 更好。不得不說,當(dāng)它涉及到彈性、并發(fā)性和擴展性時,有自己的局限。當(dāng)遇到這些限制時,Hadoop 和它的近親 Spark 是解決 BI 工作負(fù)載的好選擇。
判決:維度模型和星型模式過時了嗎?
我們都知道,Ralph Kimball 已經(jīng)退休了,但他設(shè)計原則的思想和觀念仍然是有效的,也將會繼續(xù)存在。即使我們不得不讓它們適應(yīng)新的技術(shù)和存儲類型,它們?nèi)匀荒軌驇砭薮蟮膬r值。