1. 程式人生 > >The Data Warehouse Toolkit 閱讀筆記

The Data Warehouse Toolkit 閱讀筆記

### 前言 這篇筆記的主要內容來至於[The Data Warehouse Toolkit](https://book.douban.com/subject/1441156/),該書可以稱為數倉建模的聖經 ### 什麼是星型模型 以一個業務事實為主表。比如一筆訂單就是一個業務事實。訂單有商品的SKU資訊,銷售市場資訊,日期資訊 ,這些基本屬性,叫做維度。 ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212159939-1082791799.png) #### 雪花 一個產品維度,本身還有分類、包裝等資訊,也獨立做成表,圍繞在事實表身邊,就像一片雪花。 ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212207609-2093733395.png) ### 為什麼要用星型模型 - OLTP針對的是線上事務,寫多的場景,所以粒度要細。數倉模型的應用場景是資料分析,涉及大量查詢,所以要少關聯,多整合 - 降低業務理解難度和複雜性,有些業務事實,跨了很多表,甚至跨了很多庫,比如一個訂單的生命週期,牽扯訂單團隊、倉儲團隊、物流團隊。如果不建模,則需要所有使用資料的人員明白對應的業務細節,表的資料結構 ### 三種模型型別 總結來看,事實表分為三種類型。 * 事務事實表,比如一次商品銷售記錄 * 週期快照表。按一定時間週期記錄業務實體快照。比如記錄每天的促銷商品銷售情況 * 累計快照表,記錄業務實體一些列業務流程變更的事實表 數倉的資料模型,為了應對不同的OLAP場景,往往三者皆有之。他們三者之間的區別如下 ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212224871-212593476.png) #### 累計事實模型 有些業務實體,會發生一系列的業務流程變更,在事實表中,使用一條記錄,記錄該業務實體各關鍵流程的所有資訊,並隨各業務事件的發生來更新這條記錄,也就是一條記錄會累計各種變化,叫做累計快照表。比如一個商品進入倉庫的的整個流程可能有,收貨,驗貨,裝箱,運輸等。其模型設計示例如下: ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212236095-1146742103.png) 一行資料的變更示例如下: ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212242122-1266555938.png) 累計快照事實表要保證流程節點個數確定有限。動態任意多個流程,不適合做成累計快照事實表,因為變化太頻繁。 ### 一個模型怎麼定義 - 選定業務事實 ,一條事實一定能回答誰、何時、何地、做了什麼事,為什麼要這麼做,怎麼做的?(who, when, where, what, why, and how) - 定義資料粒度,事實表的粒度一定要細,才能靈活承載更上層的各種邏輯口徑的指標計算。 - 標識維度表 - 標識事實表 ### 如何響應維度表的變化 維度表相對比較穩定,但也不是完全不會變化。比如使用者的資訊維度表,就可以變更使用者的年齡,地址等。那如何應付這些變化?主要有以下幾種 - 保留原值 - 改維度表屬性值 - 拉鍊表 - 新增新欄位記錄老資料 - 迷你維度表(Mini-Dimension) - 混合 #### 保留原值 事實表儲存原值,而不是關聯維度表。這樣當相關屬性變化時,新的事實表記錄對應的值就是新屬性值。 比如,商品當時的價格,就可以直接儲存到事實表上 #### 修改維度表屬性值 直接將維度表對應變化欄位的值進行修改。這會導致事實表中的老資料,關聯維度表,也獲取到了新值,使得報表結果出現變化,已經預計算的OLAP Cube需要重新計算等問題。所以慎用這種方式。舉例如下: ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212303487-1622016442.png) #### 拉鍊表 給維度表加上資料有效時間欄位,將維度表變成拉鍊表,使得維度表不光能體現當前的值,還能記錄歷史的資訊。示例如下: ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212312882-154567476.png) 每個記錄都有一個id,事實表關聯當時的維度id。 #### 新增新欄位記錄老資料 有的報表場景,可能就是需要根據新的維度資訊,去關聯老的的事實表記錄,且變化不會太頻繁。可以通過在維度表加欄位,記錄老資料,這樣新老資料都存在,即可滿足這種需求。示例如下: ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212322444-1985577643.png) #### 迷你維度表(Mini-Dimension) 有些維度表的資料欄位較多,且部分欄位資料變更較為頻繁,如果採用拉鍊表的方式設計,那維度表的資料,將會快速增加。所以拉鍊表不是一個好的設計。比如使用者資訊中,籍貫、民族等資訊變化較少。但使用者的收入等級、位置範圍卻會頻繁的變化。所以可以將這些變化項抽出來,做成一個迷你維度表。同時為了減少迷你維度表的資料條數。可以使用範圍做欄位值,比如一張可能的使用者迷你維度表表示例如下: ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212332024-795972887.png) 最終的模型設計如下: ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212342948-2109494657.png) 迷你維度記錄了那些變化頻繁的屬性值所有可能的組合。事實表關聯迷你維度表,即可覆蓋每一種變化需求。由於迷你維度表,使用了範圍值。所以如果想要獲取某個具體屬性值是不可能的。所以針對這種變化頻繁,資料量大的業務資料,可以想辦法做成事實表,而不是維度表。 ### 層級維度如何處理 比如一個部門,有自己的父部門,還有自己的子部門。這種層級維度,怎麼在維度表中體現?分為有限層級和無限層級。 #### 層級是少量有限 層級少量有限,比如省、市、區、縣,最多到村,不可可能無限。對於這種層級,可以在一行中,加如多個欄位來顯示。 | Province| city| zone|county| | :-------- | --------:| :------: |:------: | #### 不固定的層級 比如部門這種實體,其層級是不固定的。那通過上述一行中加欄位來應對就不現實。這個時候需要通過更抽象的父子關係,來指定兩行記錄的關係。比如加上parent key: ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212355883-1664998559.png) ### 一些建模規範 #### 打平所有的層級 有些維度資訊,本身是多層級,比如產品術語某個品牌,某個品牌屬於某個分類。他們都是多對一的關係。我們建一張產品表,不應該也按事務型資料庫中的正規化來建多張表,而是應該打平層級。方便查詢,提高效能,因為維度表資料量一般都很小,不用擔心冗餘情況。比如一個產品維度表示例如下: ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212408883-1863040441.png) #### 將編碼中隱含的資訊抽出 有些業務會有業務編碼。一個業務編碼含有多種資訊,比如前2位代表大區,中間2位代表省份等。這種資料,我們除了要儲存業務編碼外,還要將編碼中隱含的資訊,分別用欄位存放。所有的目的,都是細粒度並且直觀的存放各種資料,方便報表的計算,以及提升計算效能。 #### 避免欄位為null 如果一個欄位,特別是會用作關聯的欄位,應該儘量避免為Null。因為null會導致表關聯,或資料統計,展示時出現一些問題。所以當確實缺失值時,使用一些特殊的值作為替代,比如-1,或者一個文字去填充對應欄位。具體到維度欄位的null,可以在維度表中建立一條專門用來解釋為空的記錄,同樣用描述性文字填充這條記錄。然後在事實表中關聯這個id. #### 退化維度(Degenerate Dimensions) 事實表中的欄位,除了存放普通維度表主鍵外,還可以存放退化維度主鍵。比如事實表中有一個productId資源,對應的維度表為product表。而事實表中的一條記錄對應的業務事件,其在關係型資料庫中的的id,或者發票編號等,對報表後續計算依然是有意義的。這種欄位我們依然會存放到事實表中,作為一個維度,但它不像產品id一樣,有一個標準的維度表,我們稱這種情叫退化維度。 #### 沒有發生的事實表(Factless Fact Tables) 正常情況下,我們會按實際發生的業務事件來建模,比如有實際購買行為的促銷商品記錄。其資料模型設計如下: ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212418629-663229828.png) 但這種方式的資料模型,但該事實模型無法回答`哪一個促銷商品在指定日期沒有被購買的記錄`。因為沒有被購買,就沒有零售記錄發生,資料庫中根本就沒有資訊,也就無從統計。這個時候,我們可以增加另外一個事實表,只記錄每天參與促銷商品的資訊。同時再建一個促銷維度表,去描述具體的促銷資訊。模型設計如下: ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212427687-496658498.png) 這樣為了回答前面的問題,第一步根據日期查詢某天去促銷事實表中找出當天參與訊息的商品。第二步,從最開始建立的實際銷售事實表中找出那天實際售賣的商品。這兩部資料做一個外關聯,就可以解答該問題。 #### 維度表主鍵(Surrogate Keys) 維度表不要使用操作性資料庫中對應的ID,也即不要使用OLTP對應表的id,而要自己生成int型別的id.原因有以下幾點。 - OLTP庫主要對應線上業務的處理,其中的表結構和業務資料新增、更新頻繁,原始OLTP的表可能隨著業務量增加而分庫分表 - 維度表的資料來源,可能是OLTP中的好幾個表,本來就沒有一個固定的主鍵 - 提升效能,維度表的資料本來就不會很多,所以使用int綽綽有餘。維度表的id,會被事實表做為外來鍵儲存,事實表的資料量往往會很大,選用更小的儲存型別更節省儲存空間 - 方便做一些OLTP庫中不存在的業務記錄,比如記錄促銷維度表,記錄一個No Promotion的記錄。方便事實表關聯。 #### 事實表主鍵 雖然事實表一條記錄可以通過其儲存的各維度外來鍵組合一起來唯一確定,但最好還是想維度表主鍵一樣,單獨設一個事實表主鍵,有以下好處: - 一些處理恢復時,可以記錄斷點處的主鍵id來判斷恢復量,因為主鍵一般是有序的 - 快速唯一定位一條事實表記錄 - 在多個事實表有父子關係時,可以作為關聯鍵使用 #### 多角色維度表 事實表中可能有多個欄位,使用同一個維度表。那最後基於事實表做報表計算將會出現問題。因為不可能join同一張維度表兩次。解決辦法是基於公用維度表,建不同的維度表,讓事實表關聯這些新建的維度表,新建的維度表可以是實體表,也可以檢視。假設一個事實表有多個欄位關聯時間維度表,按照這種處理方式的建模示例如下: ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212436898-1865997365.png) 事實表中有多個extend指標屬性,其作用是將一些可以提前計算的指標,提前計算出來,提升報表效能,統一計算口徑。 前提是這些指標毫無爭議,能被廣泛使用 #### 事實表泛型粒度 比如訂單有訂單明細這個業務。 我們可以基於訂單明細事實建模型。但訂單的總金額怎麼分攤到訂單明細上,按什麼規則分攤?如果不分攤,直接將訂單總金額冗餘在訂單明細上,在有些場景做聚合計算時,會多算金額。 所以最好訂單主表事實和明細表事實都做,再將其用主外來鍵關聯。同時為了效能考慮,一些不參與計算的訂單屬性,可以冗餘到訂單明細,如果只使用訂單明細事實表時,不至於關聯太多表。下面是一個壞案例,訂單的主表的客戶資訊,沒有冗餘到明細表 ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212446273-1701867142.png) ### 實時計算與數倉 通常資料倉庫的資料都是T+1的。但有些業務場景需要實時資料。實時資料又分兩種: - 瞬時(Instantaneous),一般資料資料應用直接接入源資料的變化資料。為了減少資料延遲,中間不經過etl處理。 - 日內(Intra-Day),同普通的T+1資料處理方式一樣,只是一天之內多次去源資料拉取,然後走完整的etl流程 對於第二種,也需要建模,但其相關的維度表需要實時響應當天的資料變化 ### 巨集觀流程 #### 啟動會議,確立相關人員職責 包括,業務負責人,業務驅動人員,專案管理人員,資料架構,系統架構,業務使用者,開發人員。其中,懂業務的權利負責人確立是非常重要的。因為數倉系統的核心目的,便在於滿足業務方的需求。這需要大量了解和梳理業務,需要極富能力和權力的業務負責人。 #### 巨集觀模型梳理 ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212457230-539345760.png) ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212506607-945295955.png) 使用矩陣圖,對公司的整體業務建模,找出公共維表,統一各種業務術語。 - 如果術語不統一 數倉最終計算出的報表,可能不同團隊的名稱都不一致,出現歧義,互相不能理解,增加使用者的門檻 - 如果不統一維度 比如時間維度的不一致,比如兩個時間維度中的環比時間段不一致,可能導致最終兩張報表的同一個指標產出了不同的值。反之,統一了時間維度,使得使用相同維度表的兩個事實模型可以join分析 #### 計算選型和建模 #### 培訓使用,後期維護升級 統一各種術語 培訓各類業務人員相關係統知識,減少資訊不對稱,並時刻聽取他們的難點、痛點、進行系統迭代,升級。 ### 資料質量把控 資料的清洗,資料質量的校驗,儘量放到資料架構初始階段,及早發現問題。 資料質量問題檢查主要分為三個部分(Quality Screens): 1、欄位質量檢查,檢查非空欄位是否有空值,檢查欄位資料的格式是否滿足要求 2、資料結構檢查,比如某兩個表有父子關係,需要檢查這種父子關係是否存在 3、業務規則檢查,比如航空業務,檢查源系統給的白金使用者,是否飛行里程都達標? 對問題資料的處理有如下幾種: 1、中斷etl處理程序,不推薦,需要立刻介入解決問題,然後重啟 2、記錄問題資料和其對應的執行條件,並繼續走完etl流程,推薦,可以方便重現問題 對於錯誤資料的記錄 ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212515730-1736891156.png) ### 一些踩坑最佳實踐 ![](https://img2020.cnblogs.com/blog/2007268/202009/2007268-20200903212524436-1403454471.png) - 基礎打牢,我們一般把星型模型放在DWD層,一定要把DWD建好,萬丈高樓平地起,後續DWD模型一定要有設計,評審的流程。 - 建設資料質量系統,對數倉各層的資料質量進行監控,及時報警,資料質量管理系統應該嵌入到數倉的每一層 - 建設元資料管理系統,這個是數倉的地圖。 - 所有報表開發,原則上,不允許直接使用ODS層的表,這樣會導致資料煙囪 #### 如果沒有質量管理系統 - 報表質量在使用者側才發現問題,排查需要整條鏈路去做,排查耗時 - 排查出問題後,重跑資料,重跑耗時 #### 如果沒有元資料管理系統 - 沒有地圖數倉建設會失控,會失控,會抓瞎 - 數倉能力,口口相傳,離職就失傳 - 無法回答領導的靈魂拷問,做了多少報表? 做了什麼報表?數倉有哪些資產?