據(jù)倉(cāng)庫(kù)架構(gòu)選型與維度建模實(shí)戰(zhàn):從分層設(shè)計(jì)到模型上線全流程)
1. 數(shù)倉(cāng)架構(gòu)選型為什么你的第一個(gè)決定就決定了后面三年的維護(hù)成本很多團(tuán)隊(duì)在啟動(dòng)數(shù)據(jù)倉(cāng)庫(kù)項(xiàng)目時(shí)第一反應(yīng)是“先跑起來再說”結(jié)果半年后數(shù)據(jù)口徑混亂、報(bào)表對(duì)不上、開發(fā)效率斷崖式下跌。我見過太多這樣的案例根源往往不在技術(shù)本身而在于架構(gòu)選型階段沒有想清楚業(yè)務(wù)到底需要什么。數(shù)據(jù)倉(cāng)庫(kù)架構(gòu)本質(zhì)上解決三個(gè)問題數(shù)據(jù)從哪里來、怎么存、怎么用。聽起來簡(jiǎn)單但每個(gè)問題背后都有一堆需要權(quán)衡的選項(xiàng)。比如數(shù)據(jù)來源是只接業(yè)務(wù)庫(kù)的binlog還是也要接日志、第三方API、文件導(dǎo)入存儲(chǔ)是用Hive還是ClickHouse還是兩者配合用分層架構(gòu)還是扁平架構(gòu)這些選擇沒有絕對(duì)的對(duì)錯(cuò)只有適不適合當(dāng)前團(tuán)隊(duì)和業(yè)務(wù)階段。我個(gè)人的經(jīng)驗(yàn)是架構(gòu)選型要遵循“業(yè)務(wù)驅(qū)動(dòng)、漸進(jìn)演進(jìn)”的原則。什么意思就是不要一上來就照著大廠的架構(gòu)圖抄人家日增PB級(jí)、團(tuán)隊(duì)上百人你日增幾十GB、團(tuán)隊(duì)三五個(gè)人照搬只會(huì)把自己拖死。小團(tuán)隊(duì)初期用“ODS DWD DWS ADS”四層架構(gòu)完全夠用甚至可以把DWD和DWS合并減少數(shù)據(jù)流轉(zhuǎn)環(huán)節(jié)。等業(yè)務(wù)量上來了、團(tuán)隊(duì)規(guī)模擴(kuò)大了再逐步拆分。這里有個(gè)很關(guān)鍵的判斷依據(jù)數(shù)據(jù)消費(fèi)方的數(shù)量和需求復(fù)雜度。如果只有兩三個(gè)分析師看固定報(bào)表那ADS層直接出結(jié)果就行中間層可以簡(jiǎn)化。但如果有多達(dá)十幾個(gè)業(yè)務(wù)方、每個(gè)方都要自助取數(shù)、還要做即席查詢那DWD和DWS的明細(xì)和匯總層就必須做扎實(shí)否則每次取數(shù)都要從ODS重新加工計(jì)算資源根本扛不住。還有一個(gè)容易被忽視的點(diǎn)是數(shù)據(jù)時(shí)效性要求。T1離線跑批和準(zhǔn)實(shí)時(shí)分鐘級(jí)更新架構(gòu)差異巨大。離線場(chǎng)景下HiveSpark可以穩(wěn)穩(wěn)當(dāng)當(dāng)準(zhǔn)實(shí)時(shí)就得引入FlinkKafka存儲(chǔ)層也要換成支持upsert的格式如Hudi或Iceberg。我見過一個(gè)團(tuán)隊(duì)在離線架構(gòu)上硬改準(zhǔn)實(shí)時(shí)結(jié)果每天凌晨跑批和實(shí)時(shí)任務(wù)搶資源兩邊都跑不完。所以架構(gòu)選型前一定要把業(yè)務(wù)方對(duì)時(shí)效性的真實(shí)需求摸清楚別被“越快越好”這種模糊需求帶偏。2. 多維數(shù)據(jù)模型設(shè)計(jì)從ER圖到星型模型的思維轉(zhuǎn)換做數(shù)倉(cāng)建模和做業(yè)務(wù)系統(tǒng)建模思維方式完全不同。業(yè)務(wù)系統(tǒng)追求的是范式化盡量消除冗余保證事務(wù)一致性。數(shù)倉(cāng)追求的是查詢性能和易用性適當(dāng)冗余反而能大幅提升效率。這個(gè)思維轉(zhuǎn)換如果沒完成建出來的模型就會(huì)又難查又難維護(hù)。2.1 維度建模的核心事實(shí)與維度維度建模最核心的概念就兩個(gè)事實(shí)表和維度表。事實(shí)表存的是業(yè)務(wù)過程的度量值比如訂單金額、支付筆數(shù)、庫(kù)存數(shù)量。維度表存的是觀察這些度量的角度比如時(shí)間、地區(qū)、商品、用戶。兩者通過外鍵關(guān)聯(lián)形成星型結(jié)構(gòu)。為什么叫星型因?yàn)槭聦?shí)表在中心維度表像星星的角一樣散在周圍。這種結(jié)構(gòu)的好處是查詢路徑短事實(shí)表和維度表一次join就能出結(jié)果不像范式化模型要join七八張表。對(duì)于分析場(chǎng)景來說join次數(shù)直接決定查詢速度所以星型模型在數(shù)倉(cāng)里幾乎是默認(rèn)選擇。但星型模型也不是萬能的。當(dāng)維度表特別大、或者維度之間存在層級(jí)關(guān)系時(shí)就需要用到雪花模型。雪花模型是把維度表進(jìn)一步規(guī)范化比如商品維度拆成商品表、品類表、品牌表。這樣做的好處是節(jié)省存儲(chǔ)、減少冗余壞處是查詢時(shí)要多join幾次。我的建議是能星型就星型除非維度表大到單表放不下否則不要輕易雪花化。因?yàn)閿?shù)倉(cāng)的存儲(chǔ)成本遠(yuǎn)低于計(jì)算成本用空間換時(shí)間是劃算的。2.2 緩慢變化維處理維度變化的標(biāo)準(zhǔn)方案維度不是一成不變的。用戶會(huì)改地址、商品會(huì)調(diào)價(jià)格、門店會(huì)換店長(zhǎng)。這些變化怎么在數(shù)倉(cāng)里記錄就是**緩慢變化維SCD**要解決的問題。最常見的三種處理方式類型處理方式適用場(chǎng)景優(yōu)點(diǎn)缺點(diǎn)SCD Type 1直接覆蓋舊值維度變化不需要保留歷史實(shí)現(xiàn)簡(jiǎn)單、存儲(chǔ)省歷史數(shù)據(jù)無法追溯SCD Type 2新增一行記錄用時(shí)間戳標(biāo)記有效期需要保留完整變化歷史歷史可追溯、分析靈活表會(huì)膨脹、查詢要加時(shí)間過濾SCD Type 3增加一列存舊值只需要保留上一次的值查詢簡(jiǎn)單只能保留有限歷史實(shí)際項(xiàng)目里用得最多的是Type 2。比如用戶維度每次用戶地址變更就新增一條記錄用start_date和end_date標(biāo)記這條記錄的有效期。這樣分析“用戶下單時(shí)所在的省份”就能準(zhǔn)確關(guān)聯(lián)到當(dāng)時(shí)的地址而不是現(xiàn)在的地址。這個(gè)細(xì)節(jié)在電商、物流、金融場(chǎng)景里特別重要口徑錯(cuò)了報(bào)表就全錯(cuò)了。注意SCD Type 2的end_date通常用“9999-12-31”表示當(dāng)前有效不要用NULL因?yàn)楹芏鄶?shù)據(jù)庫(kù)對(duì)NULL的join和比較處理不一致容易埋坑。2.3 一致性維度與一致性事實(shí)保證口徑統(tǒng)一的關(guān)鍵數(shù)倉(cāng)做大了以后最怕的就是同一個(gè)指標(biāo)在不同報(bào)表里數(shù)字不一樣。比如“活躍用戶數(shù)”在銷售報(bào)表里是100萬在運(yùn)營(yíng)報(bào)表里是120萬業(yè)務(wù)方直接炸鍋。這個(gè)問題的根源往往是維度不一致或事實(shí)不一致。一致性維度是指不同事實(shí)表關(guān)聯(lián)的維度表其定義、粒度、編碼必須一致。比如時(shí)間維度不能一個(gè)事實(shí)表用自然日、另一個(gè)用工作日地區(qū)維度不能一個(gè)用注冊(cè)地、另一個(gè)用收貨地。一致性事實(shí)是指同一個(gè)指標(biāo)的計(jì)算邏輯必須統(tǒng)一不能A報(bào)表算“支付成功訂單”、B報(bào)表算“創(chuàng)建訂單”。解決這個(gè)問題的標(biāo)準(zhǔn)做法是建立總線矩陣??偩€矩陣的行是業(yè)務(wù)過程下單、支付、發(fā)貨、退款列是一致性維度時(shí)間、用戶、商品、地區(qū)、渠道。每個(gè)業(yè)務(wù)過程和哪些維度相關(guān)就打勾。這個(gè)矩陣一旦確定所有事實(shí)表和維度表的設(shè)計(jì)都必須遵循不能隨意增減維度或改變粒度。我自己的經(jīng)驗(yàn)是總線矩陣不用一開始就做得特別完美但一定要有。哪怕先用Excel畫個(gè)草圖也能在后續(xù)開發(fā)中避免大量返工。每次新增一個(gè)事實(shí)表先看總線矩陣?yán)锼鼞?yīng)該關(guān)聯(lián)哪些維度再動(dòng)手建表比拍腦袋建完再改要省事得多。3. 分層架構(gòu)落地ODS、DWD、DWS、ADS各自干什么分層架構(gòu)是數(shù)倉(cāng)工程化的基礎(chǔ)。沒有分層所有邏輯堆在一起改一個(gè)指標(biāo)要?jiǎng)邮畯埍聿橐粋€(gè)問題要翻三天代碼。分層之后每層職責(zé)清晰數(shù)據(jù)流向明確維護(hù)成本大幅降低。3.1 ODS層貼源存儲(chǔ)不做任何加工ODS層全稱Operational Data Store直譯是操作數(shù)據(jù)存儲(chǔ)。它的核心原則是貼源也就是業(yè)務(wù)庫(kù)是什么樣ODS就存什么樣不做任何清洗、轉(zhuǎn)換、聚合。這樣做的好處是當(dāng)業(yè)務(wù)庫(kù)數(shù)據(jù)出現(xiàn)問題時(shí)ODS層可以作為原始憑證進(jìn)行追溯當(dāng)上層邏輯變更時(shí)不需要重新從業(yè)務(wù)庫(kù)抽數(shù)直接從ODS重跑即可。ODS層的表命名通常加ods_前綴后面跟源系統(tǒng)名和表名比如ods_mysql_order_info、ods_log_user_behavior。分區(qū)字段一般是日期按天增量抽取。抽取方式有全量和增量?jī)煞N全量適合小表如配置表每天覆蓋增量適合大表如訂單表只抽當(dāng)天變更的數(shù)據(jù)。實(shí)操心得ODS層一定要保留原始字段名和原始格式不要自作主張改字段類型或重命名。我見過一個(gè)團(tuán)隊(duì)在ODS層就把日期字符串轉(zhuǎn)成了timestamp結(jié)果后來發(fā)現(xiàn)源系統(tǒng)時(shí)區(qū)有問題想回退都回退不了因?yàn)樵贾狄呀?jīng)丟了。3.2 DWD層清洗轉(zhuǎn)換構(gòu)建一致性事實(shí)DWD層全稱Data Warehouse Detail是數(shù)倉(cāng)的明細(xì)層。它從ODS層抽取數(shù)據(jù)做清洗、去重、字段標(biāo)準(zhǔn)化、維度關(guān)聯(lián)等操作形成一致性事實(shí)表。DWD層的粒度通常和業(yè)務(wù)過程的最小粒度一致比如訂單粒度、支付粒度、點(diǎn)擊粒度。DWD層要做的事情包括空值處理、異常值過濾、枚舉值映射、字段類型統(tǒng)一、敏感信息脫敏、維度外鍵關(guān)聯(lián)。比如ODS層的訂單狀態(tài)是中文“已支付”“待發(fā)貨”DWD層要映射成英文枚舉paid、pending_shipODS層的用戶ID是手機(jī)號(hào)DWD層要脫敏成哈希值。DWD層的表命名通常加dwd_前綴后面跟業(yè)務(wù)過程名比如dwd_order_detail、dwd_payment_detail。分區(qū)字段也是日期但分區(qū)邏輯可能和ODS不同比如ODS按抽取日期分區(qū)DWD按業(yè)務(wù)日期分區(qū)。3.3 DWS層輕度匯總提升查詢性能DWS層全稱Data Warehouse Summary是數(shù)倉(cāng)的匯總層。它從DWD層抽取數(shù)據(jù)按主題進(jìn)行輕度匯總形成寬表。DWS層的粒度比DWD粗通常是“天維度”的組合比如“天用戶商品”的購(gòu)買匯總、“天地區(qū)品類”的銷售匯總。DWS層的價(jià)值在于減少重復(fù)計(jì)算。比如分析師經(jīng)常要查“每個(gè)用戶每天的購(gòu)買金額”如果每次都從DWD層group by計(jì)算量很大。DWS層提前把這個(gè)匯總做好分析師直接查DWS表秒出結(jié)果。DWS層的表命名通常加dws_前綴后面跟主題名和粒度比如dws_user_goods_day、dws_region_category_day。注意DWS層不是越多越好。匯總粒度太細(xì)表會(huì)膨脹粒度太粗又滿足不了分析需求。我的經(jīng)驗(yàn)是先統(tǒng)計(jì)高頻查詢的group by組合只對(duì)排名前20%的組合建DWS表剩下的讓分析師從DWD層現(xiàn)算。這樣既能覆蓋大部分場(chǎng)景又不會(huì)讓DWS層變成新的維護(hù)負(fù)擔(dān)。3.4 ADS層面向應(yīng)用直接出結(jié)果ADS層全稱Application Data Store是數(shù)倉(cāng)的應(yīng)用層。它從DWD或DWS層抽取數(shù)據(jù)按具體應(yīng)用場(chǎng)景加工直接輸出給報(bào)表、大屏、API。ADS層的粒度完全由應(yīng)用決定可能是“天地區(qū)”的匯總也可能是“小時(shí)渠道”的實(shí)時(shí)指標(biāo)。ADS層的表命名通常加ads_前綴后面跟應(yīng)用名比如ads_sales_report、ads_user_dashboard。ADS層的數(shù)據(jù)通常會(huì)被同步到MySQL、Redis、ClickHouse等查詢引擎供前端直接調(diào)用。分層架構(gòu)的每一層都有明確的職責(zé)邊界不能跨層引用。比如ADS層不能直接查ODS層必須經(jīng)過DWD和DWS。這樣做雖然多了一層流轉(zhuǎn)但保證了數(shù)據(jù)血緣清晰出問題時(shí)能快速定位是哪一層的邏輯錯(cuò)了。4. 實(shí)操全流程從業(yè)務(wù)調(diào)研到模型上線前面講了架構(gòu)和模型的理論這一部分我把一個(gè)完整的數(shù)據(jù)倉(cāng)庫(kù)建模項(xiàng)目拆開按步驟說明每個(gè)環(huán)節(jié)具體怎么做。4.1 業(yè)務(wù)調(diào)研搞清楚“誰在用、用來干什么”業(yè)務(wù)調(diào)研不是走過場(chǎng)而是決定模型成敗的關(guān)鍵。我通常會(huì)問業(yè)務(wù)方三個(gè)問題你每天看哪些報(bào)表這些報(bào)表的指標(biāo)怎么算數(shù)據(jù)延遲多久你能接受第一個(gè)問題幫我識(shí)別核心業(yè)務(wù)過程。比如銷售每天看“昨日銷售額”“Top10商品”“區(qū)域完成率”那核心業(yè)務(wù)過程就是“下單”和“支付”。第二個(gè)問題幫我確定指標(biāo)口徑。比如“銷售額”是含稅還是不含稅、是否扣退款、是否包含運(yùn)費(fèi)這些細(xì)節(jié)不搞清楚后面報(bào)表對(duì)不上就是必然的。第三個(gè)問題幫我確定時(shí)效性要求。T1能接受就不做實(shí)時(shí)省下的資源可以用在別處。調(diào)研結(jié)果要整理成業(yè)務(wù)過程清單和指標(biāo)口徑文檔。業(yè)務(wù)過程清單列出所有需要建模的業(yè)務(wù)過程指標(biāo)口徑文檔記錄每個(gè)指標(biāo)的計(jì)算公式、數(shù)據(jù)來源、過濾條件。這兩份文檔是后續(xù)開發(fā)的依據(jù)也是驗(yàn)收的標(biāo)準(zhǔn)。4.2 總線矩陣設(shè)計(jì)確定一致性和邊界總線矩陣是維度建模的骨架。行是業(yè)務(wù)過程列是一致性維度交叉點(diǎn)表示該業(yè)務(wù)過程是否關(guān)聯(lián)該維度。設(shè)計(jì)總線矩陣時(shí)先確定一致性維度的列表通常包括時(shí)間、用戶、商品、地區(qū)、渠道、門店、供應(yīng)商。然后逐個(gè)業(yè)務(wù)過程打勾。打勾的原則是該業(yè)務(wù)過程發(fā)生時(shí)這個(gè)維度是否參與。比如“下單”業(yè)務(wù)過程關(guān)聯(lián)時(shí)間、用戶、商品、地區(qū)、渠道“支付”業(yè)務(wù)過程關(guān)聯(lián)時(shí)間、用戶、支付方式、渠道“發(fā)貨”業(yè)務(wù)過程關(guān)聯(lián)時(shí)間、商品、倉(cāng)庫(kù)、物流公司。每個(gè)業(yè)務(wù)過程關(guān)聯(lián)的維度可能不同但同一維度在不同業(yè)務(wù)過程中的定義必須一致。總線矩陣確定后所有事實(shí)表和維度表的設(shè)計(jì)都必須遵循。新增業(yè)務(wù)過程時(shí)先看總線矩陣?yán)锼鼞?yīng)該關(guān)聯(lián)哪些維度再動(dòng)手建表。這樣能保證不同事實(shí)表可以共享維度做交叉分析時(shí)不會(huì)出現(xiàn)口徑不一致的問題。4.3 維度表設(shè)計(jì)從業(yè)務(wù)庫(kù)到數(shù)倉(cāng)的映射維度表設(shè)計(jì)的第一步是確定維度屬性。以用戶維度為例屬性可能包括用戶ID、用戶名、手機(jī)號(hào)、注冊(cè)時(shí)間、注冊(cè)渠道、會(huì)員等級(jí)、所在城市、年齡段。這些屬性有些來自業(yè)務(wù)庫(kù)的用戶表有些需要從其他表關(guān)聯(lián)或計(jì)算得到。第二步是確定維度主鍵。數(shù)倉(cāng)里的維度主鍵通常是代理鍵surrogate key也就是自增整數(shù)而不是業(yè)務(wù)庫(kù)的自然鍵。代理鍵的好處是不受業(yè)務(wù)庫(kù)主鍵變更影響可以處理SCD Type 2的多版本記錄join時(shí)整數(shù)比字符串快。第三步是確定SCD類型。不是所有屬性都需要保留歷史。比如用戶名變更通常不需要保留歷史用Type 1覆蓋即可但會(huì)員等級(jí)變更可能需要保留歷史用Type 2新增記錄。這個(gè)決策要和業(yè)務(wù)方確認(rèn)因?yàn)門ype 2會(huì)增加表的大小和查詢復(fù)雜度。維度表建好后要定期和業(yè)務(wù)庫(kù)做數(shù)據(jù)質(zhì)量校驗(yàn)。比如用戶維度表的記錄數(shù)應(yīng)該和業(yè)務(wù)庫(kù)用戶表一致Type 1場(chǎng)景或者大于等于Type 2場(chǎng)景。如果發(fā)現(xiàn)不一致要排查是抽取遺漏還是過濾條件寫錯(cuò)了。4.4 事實(shí)表設(shè)計(jì)粒度、度量、外鍵事實(shí)表設(shè)計(jì)的第一步是確定粒度。粒度是事實(shí)表最核心的屬性它決定了事實(shí)表的一行代表什么。比如訂單事實(shí)表的粒度是“一個(gè)訂單一行”支付事實(shí)表的粒度是“一次支付一行”點(diǎn)擊事實(shí)表的粒度是“一次點(diǎn)擊一行”。粒度一旦確定就不能隨意更改因?yàn)樗卸攘慷际腔谶@個(gè)粒度計(jì)算的。第二步是確定度量。度量是事實(shí)表里的數(shù)值字段比如訂單金額、支付金額、商品數(shù)量、折扣金額。度量分為可加度量、半可加度量和不可加度量??杉佣攘靠梢园慈我饩S度匯總比如金額半可加度量只能按部分維度匯總比如庫(kù)存數(shù)量不能按時(shí)間匯總不可加度量不能匯總比如比率、百分比。第三步是確定外鍵。事實(shí)表通過外鍵關(guān)聯(lián)維度表。外鍵通常是代理鍵和維度表的主鍵對(duì)應(yīng)。如果某個(gè)維度在業(yè)務(wù)發(fā)生時(shí)不存在比如未登錄用戶的點(diǎn)擊可以用“未知”或“默認(rèn)”代理鍵填充不要留NULL。事實(shí)表建好后要做數(shù)據(jù)一致性校驗(yàn)。比如訂單事實(shí)表的訂單金額總和應(yīng)該和業(yè)務(wù)庫(kù)訂單表的金額總和一致在相同過濾條件下。如果對(duì)不上要排查是抽取遺漏、過濾條件不同、還是度量計(jì)算邏輯有誤。4.5 模型上線與迭代不是一次性的工作模型上線不是終點(diǎn)而是起點(diǎn)。上線后要持續(xù)監(jiān)控查詢性能和數(shù)據(jù)質(zhì)量。查詢性能方面關(guān)注慢查詢?nèi)罩救绻硞€(gè)DWS表查詢頻繁且耗時(shí)超過閾值考慮加索引或調(diào)整分區(qū)。數(shù)據(jù)質(zhì)量方面建立數(shù)據(jù)質(zhì)量監(jiān)控規(guī)則比如主鍵唯一性、外鍵完整性、度量非負(fù)性、分區(qū)連續(xù)性每天跑批后自動(dòng)校驗(yàn)異常時(shí)告警。模型迭代的觸發(fā)條件通常有三個(gè)業(yè)務(wù)新增了分析需求、現(xiàn)有模型無法滿足性能要求、數(shù)據(jù)源發(fā)生了變更。每次迭代都要評(píng)估影響范圍更新總線矩陣和指標(biāo)口徑文檔并通知所有下游消費(fèi)方。我見過一個(gè)團(tuán)隊(duì)改了DWD層的字段類型但沒通知下游結(jié)果ADS層的報(bào)表全部報(bào)錯(cuò)排查了半天才發(fā)現(xiàn)是上游變更導(dǎo)致的。5. 常見問題與排查技巧實(shí)錄5.1 數(shù)據(jù)口徑不一致最常見的“玄學(xué)”問題現(xiàn)象兩個(gè)報(bào)表的同一個(gè)指標(biāo)數(shù)字不一樣業(yè)務(wù)方質(zhì)疑數(shù)據(jù)準(zhǔn)確性。排查思路先確認(rèn)兩個(gè)報(bào)表的數(shù)據(jù)來源是否相同。如果都來自同一個(gè)DWS表那問題可能出在查詢條件或過濾邏輯上。如果來自不同的DWS表那問題可能出在匯總粒度或計(jì)算邏輯上。如果來自不同的DWD表那問題可能出在清洗規(guī)則或維度關(guān)聯(lián)上。常見原因時(shí)間過濾條件不同一個(gè)用下單時(shí)間一個(gè)用支付時(shí)間過濾條件不同一個(gè)包含退款訂單一個(gè)不包含維度關(guān)聯(lián)方式不同一個(gè)用inner join一個(gè)用left join度量計(jì)算邏輯不同一個(gè)算含稅金額一個(gè)算不含稅金額解決方法建立指標(biāo)口徑文檔每個(gè)指標(biāo)只允許有一個(gè)官方定義所有報(bào)表必須引用這個(gè)定義。如果業(yè)務(wù)方需要不同口徑那就定義成兩個(gè)不同的指標(biāo)分別命名不要混用。5.2 數(shù)據(jù)傾斜跑批時(shí)間越來越長(zhǎng)現(xiàn)象某個(gè)Spark任務(wù)平時(shí)跑20分鐘某天突然跑了2小時(shí)還沒結(jié)束。排查思路看Spark UI的stage詳情如果某個(gè)task處理的數(shù)據(jù)量遠(yuǎn)大于其他task那就是數(shù)據(jù)傾斜。常見原因是group by或join的key分布不均比如某個(gè)熱門商品的訂單量是其他商品的1000倍。解決方法對(duì)group by傾斜開啟Spark的spark.sql.adaptive.enabled和spark.sql.adaptive.skewJoin.enabled讓Spark自動(dòng)處理傾斜對(duì)join傾斜把大表拆成熱點(diǎn)key和非熱點(diǎn)key兩部分熱點(diǎn)key單獨(dú)處理后再union對(duì)key本身傾斜給key加隨機(jī)前綴打散處理完再去掉前綴實(shí)操心得數(shù)據(jù)傾斜預(yù)防比治理更重要。建表時(shí)就要考慮key的分布比如用戶ID通常比商品ID分布均勻能用用戶ID做分區(qū)的就不要用商品ID。5.3 緩慢變化維查詢變慢Type 2的代價(jià)現(xiàn)象用戶維度表用了SCD Type 2數(shù)據(jù)量從10萬漲到500萬關(guān)聯(lián)查詢?cè)絹碓铰E挪樗悸稵ype 2會(huì)導(dǎo)致維度表膨脹因?yàn)槊看螌傩宰兏夹略鲆恍?。如果變更頻繁表的大小會(huì)快速增長(zhǎng)。查詢時(shí)如果不過濾時(shí)間范圍就會(huì)掃描大量歷史記錄。解決方法查詢時(shí)強(qiáng)制加時(shí)間過濾條件只取當(dāng)前有效的記錄end_date 9999-12-31對(duì)維度表按start_date分區(qū)減少掃描量對(duì)不常變且不需要?dú)v史的屬性改用Type 1減少新增記錄如果歷史查詢需求很少考慮把歷史記錄歸檔到單獨(dú)的表主表只保留當(dāng)前有效記錄5.4 常見問題速查表問題現(xiàn)象可能原因排查方法解決方案報(bào)表數(shù)字對(duì)不上口徑不一致對(duì)比數(shù)據(jù)來源和過濾條件統(tǒng)一指標(biāo)口徑文檔跑批時(shí)間突增數(shù)據(jù)傾斜看Spark UI的task分布開啟自適應(yīng)執(zhí)行或打散key維度關(guān)聯(lián)查不到數(shù)據(jù)外鍵為NULL或無效檢查事實(shí)表外鍵和維度表主鍵填充默認(rèn)代理鍵分區(qū)數(shù)據(jù)缺失抽取任務(wù)失敗檢查調(diào)度日志和源系統(tǒng)狀態(tài)補(bǔ)跑失敗分區(qū)查詢超時(shí)分區(qū)過多或未加過濾看查詢計(jì)劃是否全表掃描加分區(qū)過濾或建索引6. 工具選型與團(tuán)隊(duì)協(xié)作的幾點(diǎn)體會(huì)工具選型沒有標(biāo)準(zhǔn)答案但有幾個(gè)原則可以參考。存儲(chǔ)層如果數(shù)據(jù)量在TB級(jí)以內(nèi)、查詢以聚合為主ClickHouse比Hive快很多如果數(shù)據(jù)量在PB級(jí)、需要復(fù)雜join和窗口函數(shù)Hive/Spark更合適。計(jì)算層離線跑批用Spark SQL準(zhǔn)實(shí)時(shí)用Flink即席查詢用Presto/Trino。調(diào)度層Airflow和DolphinScheduler都?jí)蛴眠x團(tuán)隊(duì)熟悉的就行。團(tuán)隊(duì)協(xié)作方面命名規(guī)范和文檔比工具更重要。表名、字段名、分區(qū)名都要有統(tǒng)一規(guī)范比如ODS層用ods_前綴、DWD層用dwd_前綴、日期分區(qū)統(tǒng)一用dt。文檔要記錄每個(gè)表的業(yè)務(wù)含義、字段說明、更新頻率、負(fù)責(zé)人。我見過一個(gè)團(tuán)隊(duì)換了三撥人因?yàn)闆]文檔新來的分析師花了兩個(gè)月才搞清楚核心表的邏輯。最后說一個(gè)我踩過的坑不要為了技術(shù)而技術(shù)。有一段時(shí)間我癡迷于用最新的數(shù)據(jù)湖格式把Hive表全改成了Iceberg結(jié)果團(tuán)隊(duì)沒人熟悉出了問題排查半天。后來回退到Hive雖然技術(shù)舊一點(diǎn)但穩(wěn)定、資料多、團(tuán)隊(duì)都會(huì)用整體效率反而更高。技術(shù)選型的第一原則是匹配團(tuán)隊(duì)能力第二原則是匹配業(yè)務(wù)階段第三原則才是技術(shù)先進(jìn)性。