① 大數據之路
人類從「IT時代」進入「DT時代」。本書介紹了阿里巴巴的大數據系統架構,為了滿足不斷變化的業務需求,同時實現系統的 高擴展性 、 靈活性 以及 數據展現的高性能 。
數據體系主要包括: 數據採集 、 數據計算 、 數據服務 和 數據應用 四大層次。
事實表包括引用的 維度 和描述具體業務的 度量 。
事實表中一條記錄描述的業務的細節程度稱為 粒度 。粒度可以使用兩種方式來表示:(1)維度屬性組合(2)所表示的具體業務含義。
事實包括可加性、半可加性和不可加性三種類型:
半可加性:只可以針對特定維度做聚合,例如庫存(不能按照日期,可按照倉庫聚合)。
可加性:可以按照任意維度聚合。
不可加性:完全不具備可加性。(例如:比率,事實表可以拆分存儲分子分母)
維度屬性也可以存到事實表中,稱為 退化維度 。
事實表有三種類型:事務事實表、周期快照事實表、累計快照事實表。
事務事實表描述的是業務過程上的原子事務,也稱為 原子事實表 。
周期快照事實表是按照周期性規律的時間間隔記錄事實。
累計快照事實表:累計快照事實表用來表示過程開始和結束過程之間的關鍵步驟事件,覆蓋整個生命周期,通常用多個日期欄位記錄關鍵時間點,記錄會隨著時間變化而修改。
事實表設計原則:
原則1: 盡可能包含所有與業務過程相關的事實。
即時存在冗餘,也盡可能存儲。
原則2:只選擇與業務過程相關的事實。
原則3:分解不可加事實為可加的組件。
例如:不存成單率,轉而存儲成單數和提單數。
原則4:選擇維度和事實前,必須先聲明粒度。
建議粒度設置的越細越好,這樣可以最大限度的提高靈活性。可以通過業務描述或者維度屬性組合的方式來定義粒度。
原則5:在同一個事實表中,不應該有不同粒度的事實。
例如:一個事實表中不應該包含某些精確到訂單粒度的度量,同時又包含只精確到城市的度量。
原則6:事實的單位一致。
原則7:盡量處理掉事實表中的null值。
sql中大於,小於的條件不適用與null值,所以盡量用數值替代null,例如0.
原則8:使用退化維度增加事實表的易用性。
在Kimball的維度設計模型中,分拆出單獨的維度表,為了節省存儲。但是為了減少使用時的關聯次數,可以多使用退化維度提供事實表易用性。
事實表設計方法:
1.選擇業務過程及確定事實表類型。2. 聲明粒度。3.確定維度。4.確定事實。5.冗餘維度(設計退化維度)。
事務事實表,即針對業務過程構建的一類事實表,用來跟蹤定義業務過程的個體行為,提供豐富的分析能力,作為數據倉庫原子的明細數據。
單事務事實表,即針對每一個業務過程設計一個事實表,這樣可以方便地對每一個業務過程進行分析研究。
表示同一個事實表包含不同的業務過程。多事務事實表有兩種實現方法:(1)使用兩個不同的事實欄位來保存各自業務過程。(2)使用同一個欄位保存,但是增加一個業務過程標簽。
下面舉例說明,淘寶交易事務事實表同時包含下單、支付和成功完結三個過程,三個過程粒度一致,可以放在一個事實表。下面確定維度和事實,該表中的下單度量、支付度量和成功完結度量信息分別存在不同欄位,如果不是當前業務處理,則用0來處理。
當不同業務過程的度量比較相似、差異不大時使用第二種事實表(使用一個欄位保存),當不同業務過程的度量差異大時,使用第一種(多欄位保存)。
對於單事務事實表和多事務事實表的選擇上,可以從以下一些方面來區分:
業務過程、粒度和維度(不同業務過程粒度相同,並且維度相似時,可以選用單事務事實表)、事實、下游業務使用、計算存儲成本。電商環境下,有父子訂單的概念,店鋪多商品各生成一個訂單,在一個店鋪合成一個父訂單。
1.事實完整性:事實表包含與其描述的過程有關的所有事實。
2.事實一致性:明確存儲每一個事實以確保度量一致性。例如,有下單商品數和商品價格2個事實,同時保存下單金額(價格*商品數)。這樣下游使用時,直接取下單金額,而不是再次計算,以保證指標的一致性。
3.事實可加性:為確保下游使用時,指標的可聚合性,盡量保存原始數,而不是計算後的比率指標。
對於事務度量,事務性事實表可以很好地表徵。但是對於一些 狀態度量 ,例如買賣家累計交易金額、商品庫存、買賣家星級、溫度(事務事實表無法聚合得到)等,事務事實表的效率較低或者無法處理。為了解決狀態度量問題,引入周期性快照事實表(也稱為 快照事實表 )。
1.用快照采樣狀態:快照事實表以預定的間隔采樣狀態度量。
2.快照粒度:快照事實表通常總是被多維聲明,即快照需要采樣的周期以及什麼將被采樣。
3.密度和稠密性:稠密性是快照事實表的重要特徵。事務事實表一般都是稀疏的,只要發生業務才會有相應記錄。
4.半可加性:快照事實表的狀態度量都是半可加的,例如商品庫存,只針對商品維度可加,對日期維度不可加。
設計快照事實表,首先確定快照粒度,然後確定采樣的狀態度量。下面介紹幾個快照事實表實例。
單維度每天快照事實表、混合維度每天快照事實表,這兩種快照表都可以從事務事實表匯總得到。另外的一種產出模式是直接使用操作型系統作為數據源來加工,例如淘寶賣家的星級評分是在操作型系統中計算得出的,倉庫直接拿來這部分數據加入事實表。全量快照事實表,是特殊類型的周期快照表,例如設計無事實的事實表來記錄評論的狀態度量。
對於研究事件之間的時間間隔需求時,累計快照事實表能較好符合需求。
特點:
1.數據不斷更新:例如,在下單、支付和確認收貨三個業務過程中,事務事實表會生成3條記錄,而累計快照表會不斷更新一條記錄(不生成新記錄)。
2.多業務過程日期:
累計快照表適用於具有較明確起止時間的短生命周期的實體,對於每個實體都經歷從誕生到消亡等步驟。
3.存儲歷史全量數據。
1.事件類的,例如瀏覽日誌。
2.條件范圍資格類的,例如客戶和銷售人員的分配情況。
主要是提前聚合,為了增加數據訪問的效率(不用再聚合了),減少數據不一致的情況。這類聚集匯總數據,被稱為「公共匯總層」。
聚集的基本步驟:1.確定聚集維度。2.確定一致性上鑽。3.確定聚集事實。
元數據主要記錄數據倉庫中模型的定義、各層級間映射關系、監控數據倉庫的數據狀態及ETL任務的運行狀態。元數據分為 技術元數據 和 業務元數據 。
阿里巴巴技術元數據包括:
數據表、列等信息;ETL作業的信息;數據同步、任務調度、計算任務等信息。數據質量和運維相關元數據。
阿里巴巴業務元數據包括:
維度屬性、業務過程、指標等。數據應用元數據,例如數據報表、數據產品等。
元數據價值:
元數據在數據管理方面為集團數據在計算、存儲、成本、質量、安全、模型等治理領域上提供數據支持。
阿里MaxCompute提供了archive壓縮方法,採用了具有更高壓縮比壓縮演算法,將數據以RAID file的形式存儲。這樣可以節省空間,但是恢復起來也更復雜,所以適用於冷備份的數據。
MaxCompute基於列存儲,通過修改表的數據重分布,避免列熱點,將會節省一定存儲空間。
存儲治理項以元數據為基礎,列出例如「62天內未訪問的分區」、「數據無更新的任務列表」等等管理項推動ETL優化。形成現狀分析、問題診斷、管理優化、效果反饋的存儲治理項優化的閉環。
生命周期管理的目的是用最少的存儲成本來滿足最大業務需求,實現數據價值最大化。
1.周期性刪除策略:
2.徹底刪除策略:主要針對無用表,ETL中間過程表。
3.永久保存策略:
4.極限存儲策略:
5.冷數據管理策略:針對重要且訪問頻率低的數據。
6.增量表merge全量表策略:
將一個數據表的成本分為存儲成本和計算成本,除此之外,上游表對該表的掃描成本也應該計入。相應的計費分別核算為:計算付費、存儲付費和掃描付費。數據資產的成本管理分為數據成本計量和數據使用計費。
② 什麼是資料庫維度 怎麼理解怎麼用做什麼用的 能否通俗易懂的說明。謝謝。
舉個簡單例子:
就拿excel表格來說,作為單一的工作表,就包含二維(行和列),而一個excel文件,通常包含多個工作表,打開excel文件時,在下方顯示的「sheet1、sheet2」這些工作表頁列,就是第三維。
excel是最簡單的資料庫應用,一個xlsx文件只有三維,但你可以用若干個xlsx文件來組成一個項目,這些文件序列,你可以視為第四維。
然後,你還可以把一組組xlsx文件放在一個個目錄中,那麼這些目錄序列,你可以視為第五維。
再往上,你還可以設置更上一級目錄,那就是第六維……
反正在excel中,任何一個單元格,都可以調用存儲在本地電腦(甚至是網路電腦)任何地方的、任何一個excel文件中的、任何一個工作表的、任何一個單元格內容,所以說,雖然是一大堆的文件,你也可以當做是一個資料庫來處理,只是不那麼方便。
……
在資料庫中,單一的資料庫就能包含很多很多維,你也可以把這些維,當做樹狀目錄的結構來理解,也可以當做一堆堆的xlsx文件集合來理解。
磁碟的存儲結構(不管是fat還是ntfs,還是linux或os或別的什麼磁碟格式),都是一種大型的、多維的資料庫,分區是一個維度,目錄是一個維度,每一檔下級目錄又是一個維度。文件是一個維度,文件中的章節行段也是維度……
數學中的維度概念,和通常意義上的空間維度,是兩回事。
空間維度可以用數學來解釋,但數學維度,三維以上你就無法用空間來顯示。
但在資料庫中,三維只是基本操作。
……
用excel來舉例,已經是我能找到的最容易理解的方案。
我真正理解資料庫維度時,是從數組開始的,當時使用一個很簡陋的編程軟體,他不提供資料庫建立和訪問,數組的維度也有限,還需要自己建立多維存儲文件,並且只支持文本格式。
文本格式中,使用【】標記數組維度,【】中間的標識符可以自定義,通過各種不同的標識符來延伸維度……做著做著,我忽然間就領悟到什麼叫資料庫、什麼叫維度,如果不考慮執行效率的話,用一個文本文件,就能模擬出一個硬碟來……
③ 影響數據檢索效率的幾個因素
影響數據檢索效率的幾個因素
數據檢索有兩種主要形態。第一種是純資料庫型的。典型的結構是一個關系型數據,比如 mysql。用戶通過 SQL 表達出所需要的數據,mysql 把 SQL 翻譯成物理的數據檢索動作返回結果。第二種形態是現在越來越流行的大數據玩家的玩法。典型的結構是有一個分區的數據存儲,最初這種存儲就是原始的 HDFS,後來開逐步有人在 HDFS 上加上索引的支持,或者乾脆用 Elasticsearc 這樣的數據存儲。然後在存儲之上有一個分布式的實時計算層,比如 Hive 或者 Spark SQL。用戶用 Hive SQL 提交給計算層,計算層從存儲里拉取出數據,進行計算之後返回給用戶。這種大數據的玩法起初是因為 SQL 有很多 ad-hoc 查詢是滿足不了的,乾脆讓用戶自己寫 map/rece 想怎麼算都可以了。但是後來玩大了之後,越來越多的人覺得這些 Hive 之類的方案查詢效率怎麼那麼低下啊。於是一個又一個項目開始去優化這些大數據計算框架的查詢性能。這些優化手段和經典的資料庫優化到今天的手段是沒有什麼兩樣的,很多公司打著搞計算引擎的旗號干著重新發明資料庫的活。所以,回歸本質,影響數據檢索效率的就那麼幾個因素。我們不妨來看一看。
數據檢索乾的是什麼事情
定位 => 載入 => 變換
找到所需要的數據,把數據從遠程或者磁碟載入到內存中。按照規則進行變換,比如按某個欄位group by,取另外一個欄位的sum之類的計算。
影響效率的四個因素
讀取更少的數據
數據本地化,充分遵循底層硬體的限制設計架構
更多的機器
更高效率的計算和計算的物理實現
原則上的四點描述是非常抽象的。我們具體來看這些點映射到實際的資料庫中都是一些什麼樣的優化措施。
讀取更少的數據
數據越少,檢索需要的時間當然越少了。在考慮所有技術手段之前,最有效果的恐怕是從業務的角度審視一下我們是否需要從那麼多的數據中檢索出結果來。有沒有可能用更少的數據達到同樣的效果。減少的數據量的兩個手段,聚合和抽樣。如果在入庫之前把數據就做了聚合或者抽樣,是不是可以極大地減少查詢所需要的時間,同時效果上並無多少差異呢?極端情況下,如果需要的是一天的總訪問量,比如有1個億。查詢的時候去數1億行肯定快不了。但是如果統計好了一天的總訪問量,查詢的時候只需要取得一條記錄就可以知道今天有1個億的人訪問了。
索引是一種非常常見的減少數據讀取量的策略了。一般的按行存儲的關系型資料庫都會有一個主鍵。用這個主鍵可以非常快速的查找到對應的行。KV存儲也是這樣,按照Key可以快速地找到對應的Value。可以理解為一個Hashmap。但是一旦查詢的時候不是用主鍵,而是另外一個欄位。那麼最糟糕的情況就是進行一次全表的掃描了,也就是把所有的數據都讀取出來,然後看要的數據到底在哪裡,這就不可能快了。減少數據讀取量的最佳方案就是,建立一個類似字典一樣的查找表,當我們找 username=wentao 的時候,可以列舉出所有有 wentao 作為用戶名的行的主鍵。然後拿這些主鍵去行存儲(就是那個hashmap)里撈數據,就一撈一個准了。
談到索引就不得不談一下一個查詢使用了兩個欄位,如何使用兩個索引的問題。mysql的行為可以代表大部分主流資料庫的處理方式:
基本上來說,經驗表明有多個單欄位的索引,最後資料庫會選一最優的來使用。其餘欄位的過濾仍然是通過數據讀取到內存之後,用predicate去判斷的。也就是無法減少數據的讀取量。
在這個方面基於inverted index的數據就非常有特點。一個是Elasticsearch為代表的lucene系的資料庫。另外一個是新銳的druid資料庫。
效果就是,這些資料庫可以把單欄位的filter結果緩存起來。多個欄位的查詢可以把之前緩存的結果直接拿過來做 AND 或者 OR 操作。
索引存在的必要是因為主存儲沒有提供直接的快速定位的能力。如果訪問的就是資料庫的主鍵,那麼需要讀取的數據也就非常少了。另外一個變種就是支持遍歷的主鍵,比如hbase的rowkey。如果查詢的是一個基於rowkey的范圍,那麼像hbase這樣的資料庫就可以支持只讀取到這個范圍內的數據,而不用讀取不再這個范圍內的額外數據,從而提高速度。這種加速的方式就是利用了主存儲自身的物理分布的特性。另外一個更常見的場景就是 partition。比如 mysql 或者 postgresql 都支持分區表的概念。當我們建立了分區表之後,查找的條件如果可以過濾出分區,那麼可以大幅減少需要讀取的數據量。比 partition 更細粒度一些的是 clustered index。它其實不是一個索引(二級索引),它是改變了數據在主存儲內的排列方式,讓相同clustered key的數據彼此緊挨著放在一起,從而在查詢的時候避免掃描到無關的數據。比 partition 更粗一些的是分庫分表分文件。比如我們可以一天建立一張表,查詢的時候先定位到表,再執行 SQL。比如 graphite 給每個 metric 創建一個文件存放採集來的 data point,查詢的時候給定metric 就可以定位到一個文件,然後只讀取這個文件的數據。
另外還有一點就是按行存儲和按列存儲的區別。按列存儲的時候,每個列是一個獨立的文件。查詢用到了哪幾個列就打開哪幾個列的文件,沒有用到的列的數據碰都不會碰到。反觀按行存儲,一張中的所有欄位是彼此緊挨在磁碟上的。一個表如果有100個欄位,哪怕只選取其中的一個欄位,在掃描磁碟的時候其餘99個欄位的數據仍然會被掃描到的。
考慮一個具體的案例,時間序列數據。如何使用讀取更少的數據的策略來提高檢索的效率呢?首先,我們可以保證入庫的時間粒度,維度粒度是正好是查詢所需要的。如果查詢需要的是5分鍾數據,但是入庫的是1分鍾的,那麼就可以先聚合成5分鍾的再存入資料庫。對於主存儲的物理布局選擇,如果查詢總是針對一個時間范圍的。那麼把 timestamp 做為 hbase 的 rowkey,或者 mysql 的 clustered index 是合適。這樣我們按時間過濾的時候,選擇到的是一堆連續的數據,不用讀取之後再過濾掉不符合條件的數據。但是如果在一個時間范圍內有很多中數據,比如1萬個IP,那麼即便是查1個IP的數據也需要把1萬個IP的數據都讀取出來。所以可以把 IP 維度也編碼到 rowkey 或者 clustered index 中。但是假如另外還有一個維度是 OS,那麼查詢的時候 IP 維度的 rowkey 是沒有幫助的,仍然是要把所有的數據都查出來。這就是僅依靠主存儲是無法滿足各種查詢條件下都能夠讀取更少的數據的原因。所以,二級索引是必要的。我們可以把時間序列中的所有維度都拿出來建立索引,然後查詢的時候如果指定了維度,就可以用二級索引把真正需要讀取的數據過濾出來。但是實踐中,很多資料庫並不因為使用了索引使得查詢變快了,有的時候反而變得更慢了。對於 mysql 來說,存儲時間序列的最佳方式是按時間做 partition,不對維度建立任何索引。查詢的時候只過濾出對應的 partition,然後進行全 partition 掃描,這樣會快過於使用二級索引定位到行之後再去讀取主存儲的查詢方式。究其原因,就是數據本地化的問題了。
[page]
數據本地化
數據本地化的實質是軟體工程師們要充分尊重和理解底層硬體的限制,並且用各種手段規避問題最大化利用手裡的硬體資源。本地化有很多種形態
最常見的最好理解的本地化問題是網路問題。我們都知道網路帶寬不是無限的,比本地磁碟慢多了。如果可能盡量不要通過網路去訪問數據。即便要訪問,也應該一次抓取多一些數據,而不是一次搞一點,然後搞很多次。因為網路連接和來回的開銷是非常高的。這就是 data locality 的問題。我們要把計算盡可能的靠近數據,減少網路上傳輸的數據量。
這種帶寬引起的本地化問題,還有很多。網路比硬碟慢,硬碟比內存慢,內存比L2緩存慢。做到極致的資料庫可以讓計算完全發生在 L2 緩存內,盡可能地避免頻繁地在內存和L2之間倒騰數據。
另外一種形態的問題化問題是磁碟的順序讀和隨機讀的問題。當數據彼此靠近地物理存放在磁碟上的時候,順序讀取一批是非常快的。如果需要隨機讀取多個不連續的硬碟位置,磁頭就要來回移動從而使得讀取速度快速下降。即便是 SSD 硬碟,順序讀也是要比隨機讀快的。
基於盡可能讓數據讀取本地化的原則,檢索應該盡可能地使用順序讀而不是隨機讀。如果可以的話,把主存儲的row key或者clustered index設計為和查詢提交一樣的。時間序列如果都是按時間查,那麼按時間做的row key可以非常高效地以順序讀的方式把數據拉取出來。類似地,按列存儲的數據如果要把一個列的數據都取出來加和的話,可以非常快地用順序讀的方式載入出來。
二級索引的訪問方式典型的隨機讀。當查詢條件經過了二級索引查找之後得到一堆的主存儲的 key,那麼就需要對每個 key 進行一次隨機讀。即便彼此僅靠的key可以用順序讀做一些優化,總體上來說仍然是隨機讀的模式。這也就是為什麼時間序列數據在 mysql 里建立了索引反而比沒有建索引還要慢的原因。
為了盡可能的利用順序讀,人們就開始想各種辦法了。前面提到了 mysql 里的一行數據的多個列是彼此緊靠地物理存放的。那麼如果我們把所需要的數據建成多個列,那麼一次查詢就可以批量獲得更多的數據,減少隨機讀取的次數。也就是把之前的一些行變為列的方式來存放,減少行的數量。這種做法的經典案例就是時間序列數據,比如可以一分鍾存一行數據,每一秒的值變成一個列。那麼行的數量可以變成之前的1/60。
但是這種行變列的做法在按列存儲的資料庫里就不能直接照搬了,有些列式資料庫有column family的概念,不同的設置在物理上存放可能是在一起的也可能是分開的。對於 Elasticsearch 來說,要想減少行的數量,讓一行多pack一些數據進去,一種做法就是利用 nested document。內部 Elasticsearch 可以保證一個 document 下的所有的 nested document是物理上靠在一起放在同一個 lucene 的 segment 內。
網路的data locality就比較為人熟知了。map rece的大數據計算模式就是利用map在數據節點的本地把數據先做一次計算,往往計算的結果可以比原數據小很多。然後再通過網路傳輸匯總後做 rece 計算。這樣就節省了大量網路傳輸數據的時間浪費和資源消耗。現在 Elasticsearch 就支持在每個 data node 上部署 spark。由 spark 在每個 data node 上做計算。而不用把數據都查詢出來,用網路傳輸到 spark 集群里再去計算。這種資料庫和計算集群的混合部署是高性能的關鍵。類似的還有 storm 和 kafka 之間的關系。
網路的data locality還有一個老大難問題就是分布式大數據下的多表join問題。如果只是查詢一個分布式表,那麼把計算用 map rece 表達就沒有多大問題了。但是如果需要同時查詢兩個表,就意味著兩個表可能不是在物理上同樣均勻分布的。一種最簡單的策略就是找出兩張表中最小的那張,然後把表的內容廣播到每個節點上,再做join。復雜一些的是對兩個單表做 map rece,然後按照相同的 key 把部分計算的結果匯集在一起。第三種策略是保證數據分布的方式,讓兩張表查詢的時候需要用到的數據總在一起。沒有完美的方案,也不大可能有完美的方案。除非有一天網路帶寬可以大到忽略不計的地步。
更多的機器
這個就沒有什麼好說的了。多一倍的機器就多一倍的 CPU,可以同時計算更多的數據。多一倍的機器就多一倍的磁頭,可以同時掃描更多的位元組數。很多大數據框架的故事就是講如何如何通過 scale out解決無限大的問題。但是值得注意的是,集群可以無限大,數據可以無限多,但是口袋裡的銀子不會無限多的。堆機器解決問題比升級大型機是要便宜,但是機器堆多了也是非常昂貴的。特別是 Hive 這些從一開始就是分布式多機的檢索方案,剛開始的時候效率並不高。堆機器是一個乘數,當資料庫本來單機性能不高的時候,乘數大並不能起到決定性的作用。
更高效的計算和計算實現
檢索的過程不僅僅是磁碟掃描,它還包括一個可簡單可復雜的變換過程。使用 hyperloglog,count min-sketch等有損演算法可以極大地提高統計計算的性能。資料庫的join也是一個經常有演算法創新的地方。
計算實現就是演算法是用C++實現的還是用java,還是python實現的。用java是用大Integer實現的,還是小int實現的。不同的語言的實現方式會有一些固定的開銷。不是說快就一定要C++,但是 python 寫 for 循環是顯然沒有指望的。任何數據檢索的環節只要包含 python/ruby 這些語言的逐條 for 循環就一定快不起來了。
結論
希望這四點可以被記住,成為一種指導性的優化數據檢索效率的思維框架。無論你是設計一個mysql表結構,還是優化一個spark sql的應用。從這四個角度想想,都有哪些環節是在拖後腿的,手上的工具有什麼樣的參數可以調整,讓隨機讀變成順序讀,表結構怎麼樣設計可以最小化數據讀取的量。要做到這一點,你必須非常非常了解工具的底層實現。而不是盲目的相信,xx資料庫是最好的資料庫,所以它一定很快之類的。如果你不了解你手上的資料庫或者計算引擎,當它快的時候你不知道為何快,當它慢的時候你就更加無從優化了。
④ 【總結】維度數據建模過程及舉例
本文介紹數據倉庫中維度數據建模的過程描述,並舉一個示例以加深對相關概念的理解。
維度模型是數據倉庫領域大師Ralph Kimall所倡導,他的《數據倉庫工具箱》,是數據倉庫工程領域最流行的數倉建模經典。維度建模以分析決策的需求出發構建模型,構建的數據模型為分析需求服務,因此它重點解決用戶如何更快速完成分析需求,同時還有較好的大規模復雜查詢的響應性能。
1、通過對業務需求以及可用數據源的綜合考慮,確定對哪種業務過程開展建模工作
2、建立的第一個維度模型應該是一個最有影響的模型——它應該對最緊迫的業務問題作出回答,並且對數據的抽取來說是最容易的。
註:粒度是指數據倉庫的數據單位中保存數據的細化或綜合程度的級別,細化程度越高,粒度就越小
1、應該先優先考慮為業務處理獲取最有原子性的信息而開發維度模型。原子型數據是所收集的最詳細的信息,這樣的數據不能再做更進一步的細分。
2、數據倉庫幾乎總是要求在每個維度可能得到的最低粒度上對數據進行表示的原因,並不是因為查詢想看到每個低層次的行,而是因為查詢希望以很精確的方式對細節知識進行抽取。
一個經過仔細考慮的粒度定義確定了事實表的基本維度特性。同時,經常也可能向事實表的基本粒度加入更多的維度,而這些附加的維度會在基本維度的每個組合值方面自然地取得唯一的值。如果附加的維度因為導致生成另外的事實行而違背了這個基本的粒度定義,那麼必須對粒度定義進行修改以適應這個維度的情景。
確定將哪些事實放到事實表中。粒度聲明有助於穩定相關的考慮。事實必須與粒度吻合。在考慮可能存在的事實時,可能會發現仍然需要調整早期的粒度聲明和維度選擇
維度建模中有一些比較重要的概念,理解了這些概念,基本也就理解了什麼是維度建模。
額,看了這一句,其實是不太容易理解到底什麼是事實表的。
比如一次購買行為我們就可以理解為是一個事實,下面我們上示例。
圖中的訂單表就是一個事實表,你可以理解他就是在現實中發生的一次操作型事件,我們每完成一個訂單,就會在訂單中增加一條記錄。
我們可以回過頭再看一下事實表的特徵,在維度表裡沒有存放實際的內容,他是一堆主鍵的集合,這些ID分別能對應到維度表中的一條記錄。
我們的圖中的用戶表、商家表、時間表這些都屬於維度表,這些表都有一個唯一的主鍵,然後在表中存放了詳細的數據信息。
下面我們將以電商為例,詳細講一下維度建模的建模方式,並舉例如果使用這個模型(這點還是很重要的)。
假設我們在一家電商網站工作,比如某寶、某東。我們需要對這里業務進行建模。下面我們分析幾點業務場景:
好,基於這幾點,我們來設計我們的模型。
下面就是我們設計出來的數據模型,和之前的基本一樣,只不過是換成了英文,主要是為了後面寫sql的時候來用。
我就不再解釋每個表的作用了,現在只說一下為什麼要這樣設計。
首先,我們想一下,如果我們不這樣設計的話,我們一般會怎麼做?
如果是我,我會設計下面這張表。你信不信,我能列出來50個欄位!其實我個人認為怎麼設計這種表都有其合理性,我們不論對錯,單說一下兩者的優缺點。
先說我們的維度模型:
再說我們這張大款表的優缺點:
數據模型的建立必須要為更好的應用來服務,下面我先舉一個例子,來切實地感受一下來怎麼用我們的模型。
需求 :求出2016年在帝都的男性用戶購買的LV品牌商品的總價格。
實現 :
維度建模是一種十分優秀的建模方式,他有很多的優點,但是我們在實際工作中也很難完全按照它的方式來實現,都會有所取捨,比如說為了業務我們還是會需要一些寬表,有時候還會有很多的數據冗餘。
⑤ 寫sql語句 一般什麼時候出現笛卡爾積啊 如何避免
樓主這個問題,表達的不是很准確。事實上你所說的什麼時候出現笛卡爾積,應該是指一對多關系的時候,如果避免重復,而不是如何避免笛卡爾積。笛卡爾積在SQL中是有特殊的關聯來求笛卡爾積的,求笛卡爾積的指令是cross join。那麼回到如何避免重復的問題上,一般對於SQL開發來說,這是讓很多人頭疼的問題。一般呢,我個人把重復定義為如下三種情況:
第一種,原數據重復,指的是對應關系表中的數據本身就存在重復。但這種情況並不多,開發的時候會設定主鍵,一般情況較少。這種情況通常把需要使用的粒度數據distinct後,再關聯就可以了。
第二種,就是維度重復。比如有區域表,分別是省市縣三列,而你要統計的是到省的數據,這樣你直接寫join的時候會直接關聯出很多條,這樣通常使用子查詢去除維度重復後,再關聯即可
第三種,就是在一對多關系關聯出來後的數據維度重復。有些東西是存放很多關系表的,在關系表關聯後出現重復數據是個很正常的事情,但是可能由於需求比較特別,這樣我們通常對這些數據進行排序組合,匯總後取數的原則,來選出我們需要的數據。
當然,說了這么多,其實怎麼寫一段SQL,還是要看需求和數據結構。具體的數據結構和具體的需求,定位了一段SQL該怎麼寫。多實踐,你就會感悟到了
⑥ 《數據倉庫工具箱》讀書筆記(一):維度建模初步
1、方便地保存數據
2、數據一致性
3、適應變化
4、及時展現數據
5、信息安全
6、數據權威
7、支撐業務
1、理解業務 理解用戶
2、為用戶提供高質量、相關的、可訪問的信息
3、維護數倉/分析環境
1、維度模型和3NF模型包含的數據是一樣的,只是維度模型存儲的數據更易理解,查詢性能更高,包裝得更靈活
事實表:
2、維度模型中的事實表來自對業務過程性能的 度量
3、事實表中每行對應一個度量事件
4、每行中的數據是一個特定級別的細節數據,稱為 粒度
5、事實表通常分為事務、累計快照、周期快照
6、事實表主鍵通常成為組合鍵
維度表:
7、維度表包含與業務過程度量事件有關的文本 環境
8、數倉分析環境取決於維度屬性的質量和深度
1、Kimball
1、收集業務需求與數據實現
2、維度設計過程:選擇業務過程、聲明粒度、確認維度、確認事實
3、業務過程是組織完成的操作型活動(訂單、注冊)
4、粒度:事務表裡的每一行表示的是什麼
5、維度:用於描述環境
6、事實:對業務過程進行度量
7、靈活擴展:事實粒度一致時可直接創建列,通過新的維度列關聯維度至事實,可以在維度表上簡歷新列添加屬性,可以使事實表粒度更原子化
1、事實錶行對應一個度量事件
2、可加、半可加是針對維度而言的,部分維度可加的是半可加。
3、事實表中的外鍵不能存在空值
4、最好保證事實度量是一致的
5、事務事實表:一行對應空間或時間上某點的度量事件,比如訂單表、日誌表
6、周期快照事實表:每行匯總了發生在某一周期的多個度量事件,比如一個用戶在一天里的點擊、退出次數
7、累計快照事實表:每行匯總了發生在過程開始和結束之間可預測步驟內的度量事件,比如訂單有提單、支付、成單、配送、評價的可作為度量的過程
8、無事實事務表:可能存在某些事件僅僅記錄 多維實體 ,沒有數字化的事實
9、聚集事實表:對原子粒度事實表數據進行上卷
感覺多數還是事務和聚集事實表
1、維度表應當具有單一主鍵列,它是扁平非規范表
2、維度表需要主鍵,可以為維度表生成無語義的整數型主鍵,可以藉助UDF來進行生成
3、操作型系統中自然鍵不能滿足需求時可以採用持久性超自然鍵
4、將常用維度退化到事實表中,清楚地表明沒有關聯的維度
5、同一維度可能存在不同的層次,一級城市,二級城市
6、可以建立將不同維度合並到一起的雜項維度,而不要為每個標識或屬性定義不同維度
7、 雪花維度: 低粒度屬性作為輔助表通過屬性鍵連接到基本維度,當這一過程中包含多重維度表層次時,建立的多級層次結構被稱為雪花模式
8、支架維度:被引用的輔助維度成為支架維度,比如銀行賬戶維度可以引用開戶日期維度
當不同的維度表的屬性具有相同列名和領域內容時,稱維度表具有一致性
1、原樣保留
2、重寫
3、增加行
4、增加新屬性(列)
1、固定深度位置層次,能夠提佛那個可預測的、快速的查詢性能
2、其他還可能存在可變深度層次、層次橋接、路徑字元屬性可變深度層次,但這些最好向固定深度層次進行統一
1、蜈蚣事實表:存在多層次維度外鍵
2、事實表也可分配代理鍵
3、多遍SQL以避免事實表間的連接
1、聚集事實也可作為維度進行處理(例如金額大於多少的用戶)
2、步驟維度:在日誌表裡可以為行為順序進行編號,探究行為發生的過程,這個維度叫步驟維度
⑦ 資料庫粒度問題
粒度問題是設計數據倉庫的一個最重要方面。粒度是指數據倉庫的數據單位中保存數據的細化或綜合程度的級別。細化程度越高,粒度級就越小;相反,細化程度越低,粒度級就越大。數據的粒度一直是一個設計問題。在早期建立的操作型系統中,粒度是用於訪問授權的。當詳細的數據被更新時,幾乎總是把它存放在最低粒度級上。但在數據倉庫環境中,對粒度不作假設。在數據倉庫環境中粒度之所以是主要的設計問題,是因為它深深地影響存放在數據倉庫中的數據量的大小,同時影響數據倉庫所能回答的查詢類型。在數據倉庫中的數據量大小與查詢的詳細程度之間要作出權衡。
⑧ 數據量大,維度多怎麼sql做查詢,
1.對查詢進行優化,應盡量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。
2.應盡量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:
select id from t where num is null
可以在num上設置默認值0,確保表中num列沒有null值,然後這樣查詢:
select id from t where num=0
3.應盡量避免在 where 子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描。
4.應盡量避免在 where 子句中使用 or 來連接條件,否則將導致引擎放棄使用索引而進行全表掃描,如:
select id from t where num=10 or num=20
可以這樣查詢:
select id from t where num=10
union all
select id from t where num=20
5.in 和 not in 也要慎用,否則會導致全表掃描,如:
select id from t where num in(1,2,3)
對於連續的數值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
6.下面的查詢也將導致全表掃描:
select id from t where name like '%abc%'
若要提高效率,可以考慮全文檢索。
7.如果在 where 子句中使用參數,也會導致全表掃描。因為SQL只有在運行時才會解析局部變數,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然而,如果在編譯時建立訪問計劃,變數的值還是未知的,因而無法作為索引選擇的輸入項。如下面語句將進行全表掃描:
select id from t where num=@num
可以改為強制查詢使用索引:
select id from t with(index(索引名)) where num=@num
8.應盡量避免在 where 子句中對欄位進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如:
select id from t where num/2=100
應改為:
select id from t where num=100*2
9.應盡量避免在where子句中對欄位進行函數操作,這將導致引擎放棄使用索引而進行全表掃描。如:
select id from t where substring(name,1,3)='abc'--name以abc開頭的id
select id from t where datediff(day,createdate,'2005-11-30')=0--『2005-11-30』生成的id
應改為:
select id from t where name like 'abc%'
select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'
10.不要在 where 子句中的「=」左邊進行函數、算術運算或其他表達式運算,否則系統將可能無法正確使用索引。
11.在使用索引欄位作為條件時,如果該索引是復合索引,那麼必須使用到該索引中的第一個欄位作為條件時才能保證系統使用該索引,否則該索引將不會被使用,並且應盡可能的讓欄位順序與索引順序相一致。
12.不要寫一些沒有意義的查詢,如需要生成一個空表結構:
select col1,col2 into #t from t where 1=0
這類代碼不會返回任何結果集,但是會消耗系統資源的,應改成這樣:
create table #t(...)
13.很多時候用 exists 代替 in 是一個好的選擇:
select num from a where num in(select num from b)
用下面的語句替換:
select num from a where exists(select 1 from b where num=a.num)
14.並不是所有索引對查詢都有效,SQL是根據表中數據來進行查詢優化的,當索引列有大量數據重復時,SQL查詢可能不會去利用索引,如一表中有欄位sex,male、female幾乎各一半,那麼即使在sex上建了索引也對查詢效率起不了作用。
15.索引並不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因為 insert 或 update 時有可能會重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有必要。
16.應盡可能的避免更新 clustered 索引數據列,因為 clustered 索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將導致整個表記錄的順序的調整,會耗費相當大的資源。若應用系統需要頻繁更新 clustered 索引數據列,那麼需要考慮是否應將該索引建為 clustered 索引。
17.盡量使用數字型欄位,若只含數值信息的欄位盡量不要設計為字元型,這會降低查詢和連接的性能,並會增加存儲開銷。這是因為引擎在處理查詢和連接時會逐個比較字元串中每一個字元,而對於數字型而言只需要比較一次就夠了。
18.盡可能的使用 varchar/nvarchar 代替 char/nchar ,因為首先變長欄位存儲空間小,可以節省存儲空間,其次對於查詢來說,在一個相對較小的欄位內搜索效率顯然要高些。
19.任何地方都不要使用 select * from t ,用具體的欄位列表代替「*」,不要返回用不到的任何欄位。
20.盡量使用表變數來代替臨時表。如果表變數包含大量數據,請注意索引非常有限(只有主鍵索引)。
21.避免頻繁創建和刪除臨時表,以減少系統表資源的消耗。
22.臨時表並不是不可使用,適當地使用它們可以使某些常式更有效,例如,當需要重復引用大型表或常用表中的某個數據集時。但是,對於一次性事件,最好使用導出表。
23.在新建臨時表時,如果一次性插入數據量很大,那麼可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果數據量不大,為了緩和系統表的資源,應先create table,然後insert。
24.如果使用到了臨時表,在存儲過程的最後務必將所有的臨時表顯式刪除,先 truncate table ,然後 drop table ,這樣可以避免系統表的較長時間鎖定。
25.盡量避免使用游標,因為游標的效率較差,如果游標操作的數據超過1萬行,那麼就應該考慮改寫。
26.使用基於游標的方法或臨時表方法之前,應先尋找基於集的解決方案來解決問題,基於集的方法通常更有效。
27.與臨時表一樣,游標並不是不可使用。對小型數據集使用 FAST_FORWARD 游標通常要優於其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的數據時。在結果集中包括「合計」的常式通常要比使用游標執行的速度快。如果開發時間允許,基於游標的方法和基於集的方法都可以嘗試一下,看哪一種方法的效果更好。
28.在所有的存儲過程和觸發器的開始處設置 SET NOCOUNT ON ,在結束時設置 SET NOCOUNT OFF 。無需在執行存儲過程和觸發器的每個語句後向客戶端發送 DONE_IN_PROC 消息。
29.盡量避免大事務操作,提高系統並發能力。
30.盡量避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。
⑨ sql 怎麼取不重復的數據的所有數據
SQL數據重復分幾種情況,一種是原數據重復,第二種是粒度重復,第三種是分布重復。
原數據重復的情況,你直接可以distinct掉,例如,學生表當中有兩個重復的學號,你想取出不重復的,直接可以寫:select
distinct
學號
from
學生表
第二種是查詢粒度重復,比如你有一張表是存儲區域的,分別為省、市、縣三列。而你需要的是只查找不同的省市,則也可以使用distinct:select
distinct
省,市
from
區域
第三種則是分布重復,比如在join
的時候,左右兩個表格存在一對多的關系,造成的重復,或者在聚合之後出現了維度重復,則這種相對來說比較麻煩,你需要在子查詢中統計或查找出唯一值,然後再去關聯,或者是按照一定的數據需求的取數規則,在查詢結果後再進行聚合,取到唯一值。
不過不管怎麼樣,都是要看實際需求是什麼樣子的。大多可以用子查詢和關聯聯合解決。