前言
最近公司在招聘數(shù)倉開發(fā)葛家,筆者負(fù)責(zé)技術(shù)方面的一些問題惦银,緩慢變化維 自然是是不可缺少的環(huán)節(jié)书蚪。
但出乎筆者預(yù)料的是殊校,所有的面試者都沒有完整了解 緩慢變化維 的前因后果及處理方式,大都是通過“野路子”碰運氣實現(xiàn)幾種簡單通用的變化方式让簿,甚至有人聲稱緩慢變化維就是拉鏈表尔当。
因此椭迎,筆者將基于 kimball 的數(shù)倉理論和自身對其的理解畜号,對緩慢變化維進(jìn)行全面且深入的介紹简软。
什么是緩慢變化維痹升?
要解釋緩慢變化維视卢,必須先解釋什么是維度。
什么是維度妒挎?
在數(shù)據(jù)倉庫的DW層中鳞芙,表根據(jù)用途往往會分為2個類型:FACT(事實表)和 DIM(維度表)原朝。
舉個例子喳坠,如果我們要描述一個餐飲過程:
? 小明 2020年4月19日下午3點20分 在 海底撈(萬達(dá)廣場) 吃了5道菜剃幌,每道菜的單價是4元负乡,總價是20元。
那么這個過程在數(shù)倉中钉答,會如此劃分:
- fact:餐飲過程数尿,單價右蹦、數(shù)量、總價
- dim:小明贷盲,餐飲時間剥扣,餐飲門店钠怯,菜名鞠鲜。
也就是說:吃了多少東西贤姆,多少錢——這些屬于fact;在哪里吃弄砍、什么時候吃?這些屬于dim输涕。
下面是簡單的ER圖音婶,方便大家更好的理解。
黃色為事實表莱坎,藍(lán)色的就是維度表衣式。
什么是緩慢變化維?
正如上述所言檐什,我們會將分析的各種角度碴卧,存放在維度表中。但正如每個人所見,維度里的數(shù)據(jù)是可能發(fā)生變化的——盡管可能跨越極久名党。
舉2個例子:
-
客戶的性別變更
可能在第一次登陸中,我們得到的信息是 該客戶性別為男。
但在幾年的客戶再一次使用中,我們又得到該客戶的性別為女。
這就是維度值的一種變化可能
性別一般并不會改變,所以大概率是其中的一次數(shù)據(jù)有誤。但也有可能是客戶做了變性手術(shù)励幼。
-
雇員的部門更替
假定有一個雇員叫小楊嵌削,他最早是負(fù)責(zé)運營的——此時他的title是"商品運營助理"艇劫;但因為某些原因浅缸,他轉(zhuǎn)組成為數(shù)據(jù)組的一員毛萌,這時title就變成了"數(shù)據(jù)分析專員"右遭。
這是緩慢變化維的一種常見可能
上面提到的這些數(shù)據(jù)變化图筹,業(yè)務(wù)系統(tǒng)(CRM娇妓、OA等)往往并不會保留歷史數(shù)據(jù)蛔钙。但在分析角度攻冷,我們是一定要保留這些改變的痕跡禁谦。這種隨著時間可能會緩慢變化的維度遥皂,就是 緩慢變化維仇祭、也就是 SCD(Slowly Changing Dimensions)
常見的處理方法
kimball整理的處理方法一共有8種,但往往只有3種被詳細(xì)使用潘靖。
類型1 重寫
與業(yè)務(wù)數(shù)據(jù)保持一致宣决,直接update為最新的數(shù)據(jù)壶熏。
這種方法主要應(yīng)用于以下兩種情況:
- 數(shù)據(jù)必須正確——例如用戶的身份證號妻枕,如需要更新則說明之前錄入錯誤剑梳。
- 無需考慮歷史變化的維度——例如用戶的頭像url,這種數(shù)據(jù)往往并沒有分析的價值柠偶。因此不做保留摇邦。
這種處理方式的優(yōu)缺點:
- 優(yōu)點:
- 簡化ETL——直接update即可铛绰。
- 節(jié)省存儲空間——其他存儲方法都占用更多空間吏垮。
- 缺點:
- 無法保留歷史痕跡——萬一有天想分析呢痹换?
類型2 增加新行
更新歷史數(shù)據(jù)時間戳,新增新行記錄新值澎迎。
這種方法主要用于 僅需要保存歷史數(shù)據(jù) 的業(yè)務(wù)場景
具體的ETL則如下:
自然鍵即指有業(yè)務(wù)意義的唯一ID氛什,例如用戶ID、身份證號等。代理鍵則可以簡單理解為該表的自增ID值
-
自然鍵第一次出現(xiàn)時。
新增一行數(shù)據(jù),created為業(yè)務(wù)系統(tǒng)的創(chuàng)建時間趣兄,updated為9999-12-31
數(shù)倉的規(guī)范不允許數(shù)據(jù)存在NULL值的情況彻舰,因此用9999-12-31代替
-
類型2的維度發(fā)生變化時
將自然鍵當(dāng)前記錄的updated由9999-12-31刷為最新時間
新增一行記錄允青,記錄最新的數(shù)據(jù),created為最新時間,updated為 9999-12-31
這樣一來,因為事實表存儲的是維度表的代理鍵而非自然鍵恐疲,因此在歷史數(shù)據(jù)的查詢中會以歷史的維度值進(jìn)行計算。同時在維度值更新后的相關(guān)數(shù)據(jù)自然使用的是新的代理鍵津肛。完美的解決了大部分緩慢變化維情況笔呀。
類型3 增加當(dāng)前值屬性
在大部分的維度模式中房蝉,很多的源數(shù)據(jù)變化將產(chǎn)生類型1和類型2變化僚匆。有時兩種技術(shù)都不能滿足需求——當(dāng)需要分析所有 伴隨著新值或舊值的變化前后 記錄的事實時,需要采用類型3變化搭幻。
很多人都難以理解類型3的重要性咧擂,因此筆者舉一個例子——一個無法用類型1和類型2處理的例子:
假定一家公司的銷售是按照銷售區(qū)域進(jìn)行分組:
突然有一天,領(lǐng)導(dǎo)靈機一動檀蹋,決定 精細(xì)化銷售松申,將東部、南部俯逾、北部重新劃分為東南贸桶、東北部
但由于發(fā)送的過于倉促,因此銷售人員是立刻使用了新的部門劃分桌肴;但同樣希望保留舊的名稱——至少要暫時保留皇筛,用以比較今年和去年的業(yè)績。即:
- 擁有使用 新區(qū)域 分析所有事實的能力识脆,無論變化前還是變化后
- 擁有使用 舊區(qū)域 分析所有事實的能力设联,無論變化前還是變化后
第一個需求——新區(qū)域分析——允許立即采用新的分組,所有歷史訂單都能分為東南灼捂、東北等新類別离例;
第二個需求——舊區(qū)域分析——允許公司采用舊分組,所有的訂單可以根據(jù)舊值分組——就好像一切都沒發(fā)生過變化悉稠。
這時宫蛆,就會發(fā)生一些問題:先前的技術(shù)不適合——無論是類型1還是類型2,都不能同時滿足這兩個需求的猛;
- 類型1可以滿足第一種需求耀盗,使用新值寫舊值。但顯然它無法實現(xiàn)第二個需求卦尊;
- 類型2則更糟叛拷,它不能滿足任意一個需求——舊的事實和舊的維度相連;而新的維度值和以后的事實相連岂却。毫無疑問忿薇,它既不能分析舊數(shù)據(jù)、也不能分析新數(shù)據(jù)躏哩。
此時引入 類型3 處理方法:新增字段同時儲存新舊值署浩。
如果發(fā)生第二次變化,當(dāng)前的current會被更新到previous中扫尺,新的變化值則會寫入current筋栋。
類型3 不保存事實的歷史內(nèi)容
需要注意的是,類型3 的改變往往并不是一個僅此一次的過程——它能發(fā)生1次就有可能發(fā)生2次甚至更多次正驻。類型3 變化只保護(hù)變化屬性的一個舊版本弊攘,一旦發(fā)生第二次變化抢腐,第一次變化前的值就要被廢棄了。如果想要用變化3 來實現(xiàn)更多的版本肴颊,那只能增加更多的列來實現(xiàn)(例如dpt_2018,dpt_2019)——這無疑是非常愚蠢的氓栈。因此,除非特定需要婿着,應(yīng)盡量避免使用類型3的變化授瘦。
其他類型
剩下的5種類型基本都不被采用,但值得一提竟宋。
類型0 不做調(diào)整
這里的數(shù)據(jù)定義與類型1類似提完,但不同點在于 類型0絕不允許ETL對該維度進(jìn)行更新——你真要改的話就手動改表吧。
例如數(shù)倉中的代理鍵
類型4 微型維度
當(dāng)變化頻率加快時候,并且維度表包含幾百萬行的維度表。如果對變化的跟蹤采用可靠的SCD2技術(shù)對瀏覽和查詢性能具有負(fù)面影響——太多行且無必要锋玲。采用新的獨立的維度表消除頻繁分析或者頻繁變化的屬性,這一維度技術(shù)叫做微型維度打肝。
例如employee的 年齡、薪資挪捕、稅收金額
年齡每年一變粗梭,薪資、稅收金額也經(jīng)常改變
這里要注意:
-
這些“易變化”的值并非存儲其準(zhǔn)確的值级零,而是其范圍值断医;
例如年齡,我們不會存“23"奏纪,而是會存"20-30"鉴嗤。收入我們不會存”999“,而是”0-1000“序调。如果存準(zhǔn)確值的話醉锅,數(shù)據(jù)量會過于高。從分析角度发绢,我們往往也只需要一個模糊的范圍即可硬耍。
如果需要記錄準(zhǔn)確值,可以考慮使用 無事實的事實表 單獨記錄朴摊。
-
微型維度沒有自然鍵,只有值的笛卡爾積組合此虑。
你不會在表中看到 user_id 這種自然鍵甚纲,因此類型4中微型維度只能在事實表中出現(xiàn)。
如果想把維度表和相關(guān)的微型維度連起來朦前,那就是類型5了介杆。
常見的微型維度表結(jié)構(gòu):
可以看到鹃操,該微型維度是由 年齡和薪資的笛卡爾積組合構(gòu)成。
你可以在建立時就將所有可能組合都預(yù)計算存入——缺點是表一開始就較大春哨,但優(yōu)點是省去了ETL的功夫
也可以出現(xiàn)一個存一個荆隘,維度表的稀疏性表明了實際數(shù)據(jù)量并不會那么多。
常見的維度表赴背、微型維度椰拒、事實表組合:查看餐飲時雇員的職位和年齡。
可以看到凰荚,微型維度表與維度表通過事實表相連燃观,并不直接連接。
類型5 類型1+微型維度
類型5便瑟,即是將類型4與類型1組合起來的方法合并缆毁。
該技術(shù)的特點是增加當(dāng)前微型維度主鍵作為主維度的一個屬性。該屬性在主維度中以類型1進(jìn)行變化更新——從而避免主維度表行的爆炸增長
這樣一來:
- 可以從主維度表獲取到其對應(yīng)的微型維度數(shù)據(jù)——雖然只有最新的到涂。
- 可以從相關(guān)事實表中獲取微型維度歷史變更的信息脊框。
類型6 類型1+類型2+類型3
類型6,即是將類型1践啄、2浇雹、3的聯(lián)合使用。
主要解決的業(yè)務(wù)場景是:
- 該維度列變化頻次較高往核,但即使這樣也希望歷史業(yè)務(wù)能以最新的值來分析箫爷。
- 無法確定每次該維度的變更時間。
- 希望保留歷史數(shù)據(jù)方便追溯
下圖為例:
ETL步驟:
- 記錄需要如此處理的列聂儒,分別創(chuàng)建previous和current兩列虎锚。
- 新數(shù)據(jù)插入時,current = previous.
- 該列有新值時衩婚,
- 歷史previous使用類型2方法處理——舊數(shù)據(jù)只處理updated窜护,新行存儲新值。
- 當(dāng)前current使用類型1方法處理——所有該自然鍵的值都刷成最新值非春。
類型7 雙類型1+類型2
在上面的類型6有一個缺點——需要額外增加一個current列柱徙。
如果有大量的維度都需要如此處理的話就會有問題:假定該維度表有150列,如果我們都用類型6處理奇昙,則會變成300列——這無疑是令人無法接受的护侮。
類型7就是 解決以上困難的。有很多種實現(xiàn)方法储耐,下面會介紹2種比較常見的
-
雙重外鍵——應(yīng)用于類型1&類型2的維度表
事實表對于該維度表存儲2個外鍵羊初,如下圖
這么一來,如果想要了解雇員在用餐發(fā)生瞬間歷史的狀態(tài),關(guān)聯(lián)至左邊的維度表即可得知长赞;如果想要以雇員最新的狀態(tài)進(jìn)行分析晦攒。則直接取右邊的表即可。
最新視圖獲取:右邊表可以通過視圖展示(比如只取updated == '9999-12-31'的數(shù)據(jù))得哆,也可以生成一個實際表來存儲脯颜。
視圖主鍵ID:對應(yīng)的主鍵current_employee_id則有多種取法,筆者建議使用超自然鍵贩据,如系統(tǒng)無超自然鍵的話也可以用普通的自然鍵來替代栋操。
超自然鍵:更為持續(xù)的自然鍵;一般自然鍵是由OLTP系統(tǒng)生成乐设,但他們有可能發(fā)生改變——例如員工離職又入職讼庇,他的userid自然會變化。因此近尚,完整的OLAP系統(tǒng)會自己生成一個和實際事務(wù)對應(yīng)的自然鍵——即超自然鍵蠕啄。
-
單外鍵
也可以更節(jié)省一些——事實表連外鍵都不需要增加。
這種處理方法的主要難點在于右表——不再只需一個自然鍵戈锻。
下圖是左表(實際維度表)的變化——標(biāo)準(zhǔn)的類型1&類型2
下圖是右表(最新視圖)的變化
可以看到歼跟,在右表的所有相關(guān)字段中,維度列無論是類型1格遭、2都更新成了最新的值哈街。
雙重外鍵的處理方法需要事實表多一個值,單重外鍵則需要ETL制造出一個存儲最新值的維度表(邏輯較復(fù)雜拒迅,且查詢會較慢)骚秦。具體采用哪種方法需要視具體業(yè)務(wù)場景
總結(jié)
對以上類型做一個總結(jié)
SCD類型 | 維度表行動 | 對事實分析的影響 |
---|---|---|
類型0 | 屬性值無變化 | 事實與原始值相關(guān)聯(lián) |
類型1 | 重寫屬性值 | 事實與當(dāng)前值相關(guān)聯(lián) |
類型2 | 為新屬性值增加新行 | 事實與發(fā)生時的有效值關(guān)聯(lián) |
類型3 | 增加新列來存儲當(dāng)前和原先值 | 事實與當(dāng)前和先前值關(guān)聯(lián) |
類型4 | 增加對快速變化維的微型維度 | 事實與有效的變化范圍關(guān)聯(lián) |
類型5 | 增加類型4和主維度表的類型1外鍵 | 事實與有效的變化范圍關(guān)聯(lián) |
類型6 | 用類型3方法新增列,歷史值用類型2處理璧微,當(dāng)前值用類型1處理 | 事實與有效的變化范圍作箍、當(dāng)前值關(guān)聯(lián) |
類型7 | 增加儲存最新值的維度表或視圖 | 事實與有效的變化范圍、當(dāng)前值關(guān)聯(lián) |
其他雜談
緩慢變化維的處理前硫,可以說是數(shù)倉的最基本能力要求之一胞得。類型1、2屹电、3是基本阶剑;類型4、5危号、6牧愁、7則是拓展。