搭建Hive數據倉庫爬過的坑-數據倉庫設計要點

開篇

基於大數據的時代背景,分佈式計算框架已經是無可替代的計算工具。那麼數據倉庫的運行環境就不只是拘泥於關係型數據庫了,在數據量比較大的前提下,分佈式計算將會比關係型數據庫更勝一籌。

那麼數據倉庫環境從關係型數據到分佈式計算框架的遷移過程中要考慮哪些問題或者需要解決哪些問題,下面我們具體詳細講解。

數據倉庫環境: Hadoop + HDFS + Hive

數據倉庫整體架構圖

在這裏插入圖片描述

問題清單(本文會依次講解下面所有問題)

  • 1 HDFS是基於文件存儲的,那麼列的分隔符應該怎麼設定?
  • 2 在hive中表的類型怎樣選擇?分區又該怎樣設置?
  • 3 hive直接操作文件,那麼它的增刪改怎樣實現?
  • 4 ETL過程中數據抽取和轉化應該怎樣實現?
  • 5 元數據怎樣管理?
  • 6 數據分層應該怎樣設計?
  • 7 緩慢變化維(SCD)的處理方法
  • 8 計算效率怎樣提升?hive計算邏輯都做了哪些優化
  • 9 遇到過的哪些問題彙總?

問題回答:

1 HDFS是基於文件存儲的,那麼列的分隔符應該怎麼設定?

整個數據倉庫的數據都是存儲於HDFS上,HDFS是分佈式文件系統,所以底層的數據是以文件的形式存在,那麼文件要和HIVE中的表字段對應上就必須按照一定的分隔符分割,所以分隔符的選取是十分重要的。如果選取的分隔符在某個字段的內容中出現,就會導致表字段錯位,導致查表時數據異常。
我們在數據倉庫中指定的分隔符如下:

row format delimited fields terminated by '\u0007'

控制字符“^G” 也就是“\u0007” 是個不可見字符。在linux環境下cat 和 more命令下是看不見此分隔符的。只有在vim命令下才可以看見“^G”的分隔符。這樣就能避免在字段內容裏出現分隔符而導致的數據異常問題。

2 在hive中表的類型怎樣選擇?分區又該怎樣設置?

hive的表有兩種類型,一種是管理表,另一種是外部表。其中最大的區別就是外部表可以在drop table後HDFS上的數據依然不丟失。而管理表在執行drop語句後表結構和表數據都會刪除。考慮到我們在ETL操作過程過程中可能會使用刪除操作,並且爲了保證數據不輕易丟失,我們決定使用外部表。另外,我們的集羣的存儲量是絕對夠用的。
hive的分區設置是非常重要的。因爲選擇分區後可以縮減查詢範圍,大幅度的提高查詢效率。一般的數據倉庫設計中基本上都會是以時間作爲分區標準,因爲在數據倉庫整個時間週期的前提下,一般都是T+1完成統計計算。所以數據的抽取和計算都是以固定的時間週期爲基準。例如:設計表類型有日表,周表,月表,年表等。所以分區一般都是時間。大部分爲日分區表,還有周分區、月分區、年分區。

partitioned by(etl_date string) 
partitioned by(etl_month string) 
partitioned by(etl_year string)

3 hive直接操作文件,那麼它的增刪改怎樣實現?

新增記錄記錄是沒有什麼特殊操作的,對於HIVE而言,新增就是在HDFS邏輯地址下添加文件。
刪除記錄:無法進行固定行刪除,HIVE中刪除的策略就是先讀取全部數據,在sql腳本或者mapreduce邏輯中添加過濾條件,篩除調待刪除的記錄後再重新複寫回源表
修改操作:無法進行固定行修改。修改方案可以有兩種:一種就是使用mapreduce程序,通過判斷進行修改再寫回表中;另一種就是先新增再刪除,新增一條修改後的記錄put到HDFS,通過sql或者mapreduce過濾掉邏輯主鍵相同更新時間老的那一條記錄,保留新增的那一條記錄即可完成

aa,bb,cc,1,2019-07-19 12:00:00 --此記錄會進行更新操作,被刪除掉
aa,bb,cc,3,2019-07-19 15:00:00 --若邏輯上聯合主鍵下標爲0,1,2那麼此條記錄會保留
aa,bb,dd,4,2019-07-19 18:00:00 --主鍵不重複,保留此記錄

4 ETL過程中數據抽取和轉化應該怎樣實現?

數據抽取分爲兩類:
       1 直接從關係型數據庫進行數據抽取
       2 直接ftp數據文件

對於第二類數據抽取就比較簡單,直接從服務器上ftp文件就可以完成數據抽取。
相比之下第一類就是比較普遍也是比較複雜一點。直接從關係型數據庫抽取數據可以可分爲兩種情況:第一是全量抽取,第二是增量抽取。
轉化: 轉化的過程是強依賴元數據管理。整個表的字段映射過程都是屬於元數據管理。
一般將這類映射關係,配置關係等都會插入到配置表中,查詢響應較快,便於管理。
轉化的過程就是存在數據的規範化,將數據按照數據倉庫的主題域、命名規範、轉化規則等導入到數據倉庫中。

轉化規則類型一:硬編碼轉化,若出現異常則以“@”開頭插入數據,便於後續查找知曉問題錯誤根源
CASE COLUMN WHEN 1 THEN '2' WHEN -1 THEN '4' WHEN -2 THEN '5' ELSE concat('@',trim(cast(COLUMN as char(2)))) END

轉化規則類型二:字段加前綴或者後綴
concat('S01','_',TRIM(ID)) 

轉化規則類型三:兩個字段進行拼接                                  --  
concat(TRIM(ID),'_',TRIM(ID2))

5 元數據怎樣管理?

元數據的管理是非常重要的,因爲在整個數據倉庫過程中都是依賴元數據。在實際工作中元數據可以分爲兩部分進行管理: 首先考慮的就是關係型數據庫存儲,元數據一般都是比較規範化的規則數據,將數據以數據庫表的形式存儲最好不過,可以提高查詢響應效率,也可以分類入表便於管理。另外一種管理方法就是EXECL,直接明瞭一目瞭然

個人建議:元數據管理可以使用EXECL和配置數據庫同時使用,EXECL可以在本地進行版本控制,將新增數據先存入EXECL生成插入表的sql語句,檢查無誤後可以導出sql語句在配置庫中執行。相當於EXECL是一個配置庫的前端顯示,便於用戶查看,也能快速的進行批量操作。

元數據涉及的流程有以下幾個方面:
1 數據抽取時的卸數規則,包括:卸數的表名,字段名,字段轉換邏輯,數據篩選邏輯(where條件),卸數的數據週期
2 數據加載: 加載的表名對應的數據文件名。
3 數據轉化: 數據轉化邏輯類型,全量刪除後全量插入類、按照邏輯主鍵更新插入類、歷史拉鍊表類。不同的轉化邏輯對應的計算過程可以通過sql腳本或者mapreduce程序實現

6 數據分層應該怎樣設計?

分層的好處:
        1 以空間換時間,通過大量的預處理來提升用戶體驗和查詢效率。
        2 簡化數據清洗過程,明確數據轉化過程,便於處理數據異常

分層
1 近源層(或者臨時存儲層,或者ODS): 可提供數據清洗異常查詢,可提供業務線人員的明細數據提取
2 數據倉庫(DW層):數據是分主題的,集成的,便於數據分析
3 數據集市(DW層):數據集市通過存儲預處理的冗餘數據,進行快速的報表查詢。數據提取,決策分析

7 緩慢變化維(SCD)的處理方法

緩慢變化維: 在實際情況下,維度的屬性並不是靜態的,它會隨着時間的流失發生緩慢的變化
處理方法:

  • 1.新信息直接覆蓋舊信息 C1 全刪全插
  • 2.對主鍵進行update操作 C2 先刪後插
  • 3.對屬性進行歷史拉鍊 C3 標準拉鍊 將變化的數據先關舊鏈,後開新鏈

8 計算效率怎樣提升?hive計算邏輯都做了哪些優化?

優化點:
  • 1 HIVE表進行分區、分桶設置,減少無關的數據參與計算。
  • 2 列裁剪。對於大數據量的表查詢時要篩選列,減少參與計算的數據量。
  • 3 拆分表。對於數據量大並且日增數據量也非常大的表進行拆分。拆分的形式比較多樣化,可以做全量表和精簡表的拆分、分月的滾動表拆分等。
  • 4 合併小文件。在計算時可能會出現大量的小數據量文件,導致任務多,計算量小,效率卻不高。合併文件後可以提高計算效率。
  • 5 儘量避免數據傾斜。
  • 6 優化count(distinct x)
  • 7 設置適當的map和reduce數量,提高計算效率
  • 8 優化sql語句,減少執行的job數量,縮短計算時間
  • 9 使用動態分區策略
  • 10 使用mapreduce程序進行數據去重
  • 11 ETL過程中添加併發操作(shell腳本添加併發處理)
  • 12 編寫UDF和UDAF函數提高執行效率

9 遇到過的哪些問題彙總?

  • 1.數據爲null時,UDF函數報錯,沒有拋出異常值。UDF代碼如下:
public class ToTimestamp extends UDF {
	
    	public static Timestamp evaluate(String str , String pattern) throws Exception{ 
    		SimpleDateFormat sdf = new SimpleDateFormat(); 
    		sdf.applyPattern(pattern);
    		Date datestr = sdf.parse(str);
    		SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    		//System.out.println(sdf2.format(datestr));
    		return Timestamp.valueOf(sdf2.format(datestr));
    }
 
	public static void main(String[] args) throws Exception{ 
		System.out.println(evaluate("null","yyyyMMddHH"));
	}

}
Exception in thread "main" java.text.ParseException: Unparseable date: "null"
	at java.text.DateFormat.parse(DateFormat.java:337)
	at com.umpay.udfs.fun.ToTimestamp.evaluate(ToTimestamp.java:22)
	at com.umpay.udfs.fun.ToTimestamp.main(ToTimestamp.java:33)
  • 2 動態分區創建數超過系統默認設置,錯誤如下:
Caused by: org.apache.hadoop.hive.ql.metadata.HiveFatalException: [Error 20004]: Fatal error occurred when node tried to create too many dynamic partitions. The maximum number of dynamic partitions is controlled by hive.exec.max.dynamic.partitions and hive.exec.max.dynamic.partitions.pernode. Maximum was set to: 256
    at org.apache.hadoop.hive.ql.exec.FileSinkOperator.getDynOutPaths(FileSinkOperator.java:933)
    at org.apache.hadoop.hive.ql.exec.FileSinkOperator.process(FileSinkOperator.java:709)
    at org.apache.hadoop.hive.ql.exec.Operator.forward(Operator.java:837)
    at org.apache.hadoop.hive.ql.exec.SelectOperator.process(SelectOperator.java:88)
    at org.apache.hadoop.hive.ql.exec.Operator.forward(Operator.java:837)
    at org.apache.hadoop.hive.ql.exec.TableScanOperator.process(TableScanOperator.java:97)
    at org.apache.hadoop.hive.ql.exec.MapOperator$MapOpCtx.forward(MapOperator.java:162)
    at org.apache.hadoop.hive.ql.exec.MapOperator.process(MapOperator.java:508)

解決方法:

SET hive.exec.dynamic.partition=true;
SET hive.exec.max.dynamic.partitions=2048;
SET hive.exec.max.dynamic.partitions.pernode=2048;
  • 使用 with cube、grouping sets 函數時報錯
An additional MR job is introduced since the cardinality of grouping sets is more than hive.new.job.grouping.set.cardinality. 
This functionality is not supported with distincts. 
Either set hive.new.job.grouping.set.cardinality to a high number (higher than the number of rows per input row due to grouping sets in the query), 
or rewrite the query to not use distincts. The number of rows per input row due to grouping sets is 32

解決方法:

set hive.new.job.grouping.set.cardinality = 1000; 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章