天天看點

《Hive權威指南》第九章:模式設計9 模式設計

文章目錄

  • 9 模式設計
    • 9.1 按天劃分的表
    • 9.2 關于分區
    • 9.3 唯一鍵和标準化
    • 9.4 同一份資料多種處理
    • 9.5 對于每個表的分區
    • 9.6 分桶表資料存儲
    • 9.7 為表增加列
    • 9.8 使用列式存儲
      • 9.8.1 重複資料
      • 9.8.2 寬表
    • 9.9 (幾乎)總是使用壓縮

9 模式設計

hive看上去與實際操作都像一個關系型資料庫,但是事實上Hive是反模式。

9.1 按天劃分的表

對于資料集增長很快的情況,可以使用這種方式,在表名中加入一個時間戳,例如upply_2020_05_20、upply_2020_05_21等。

當然對于hive,這種情況應該使用分區表。Hive通過WHERE子句來選擇需要查詢的分區。

9.2 關于分區

通過過濾分區可以優化Hive的查詢,避免全表掃描。

每一個分區一般會對應着包含多個檔案的檔案夾,如果表使用了動态分區,可能會建立非常多分區這又可能會導緻産生很多的小檔案,最終導緻NameNode挂掉。

HDFS檔案系統NameNode必須将所有系統檔案的中繼資料資訊儲存在記憶體中,每個檔案的中繼資料資訊大約150個位元組。MapR和Amazon S3就沒有這個限制。

MapReduce會将一個任務(job)轉換成多個任務(task)。預設情況下,每個task都是一個新的JVM執行個體,都需要開啟和銷毀。對于小檔案,每個檔案都會對應一個task。在某些情況下,JVM開啟和銷毀的時間中銷毀可能比實際處理資料的時間還長!是以必要的時候開啟JVM重用,也是有效的調優方式。

理想情況下分區方案不應該導緻産生太多的分區和檔案夾目錄,并且每個目錄下的檔案應該足夠大,應該是檔案系統中塊打下的若幹倍。

在設計分區表時,盡量考慮分區數量的增長是“均勻的”,而且每個分區下包含的檔案大小至少是檔案系統中塊的大小至少是檔案系統中塊的大小或塊大小的數倍。同時有必要考慮這種粒度級别在未來是否是适用的,必要的情況可以建立多級分區。例如一張按照時間和地區進行劃分。

終極目标就是優化查詢性能

9.3 唯一鍵和标準化

Hive沒有主鍵或基于序列秘鑰生成的自增鍵的概念。

在很多大資料場景,星型架構類型設計并非最優的。其主要原因是為了最小化磁盤尋道時間,我們可以避免标準化,例如傳統資料庫需要使用外鍵的關系的情況。

标準化:值資料庫組織資料的過程。

原則:

  1. 根據設計規則建立表并建立表之間的關系
  2. 取消備援度與不一緻相關性

非标準化資料允許被掃描或寫入到大的、連續的磁盤存儲區域,進而優化磁盤驅動器的***I/O***性能。當然标準化資料可能會導緻資料重複,更嚴重的場景是,導緻資料不一直的風險。加大了資料管理的難度。

9.4 同一份資料多種處理

Hive本身提供了一個獨特的文法,它可以從一個資料源産生多個資料聚合,而無需每次聚合都要重新掃描一次。對于大的資料輸入集來說,這個優化可以節省非常可觀的時間。具體示例如下:

hive> INSERT OVERWRITE TABLE sales
	> SELECT * FROM history WHERE action='purchased';
hive> INSERT OVERWRITE TABLE sales
	> SELECT * FROM history WHERE action='returned';
           

使用下面這種方式,隻需要掃描history表一次即可:

hive> FROM history
	> INSERT OVERWRITE sales SELECT * WHERE action='purchased'
	> INSERT OVERWRITE credits SELECT * WHERE action='returned'
           

9.5 對于每個表的分區

很多ETL處理過程會涉及多個處理步驟,每個步驟又會産生一個或多個臨時表,這些表僅供下一個job使用。對臨時表進行分區也是很有必要的,比如某一天的資料有問題,就不用從資料源重跑所有資料,也不會因為下一次的任務而OVERWRITE臨時表。同時運作兩個執行個體,隻要指定不同的分區即可。

一個更魯棒性的處理方法就是整個過程中使用分區。這樣就不會存在同步問題,同時也允許使用者對中間資料按日期進行比較。缺點就是需要管理中間表,以及對舊的分區資料的删除。

9.6 分桶表資料存儲

分區提供了一個隔離資料和優化查詢的便利方式。不過并非資料集都能形成合理的分區,就像前面提到的動态分區場景。

分桶是将資料集分解成更容易管理的若幹部分的另一個技術。

例如:

hive> CREATE TABLE weblog (user_id INT, url STRING, source_ip STRING)
	> PARTITIONED BY (dt STRING)
	> CLUSTERED BY (user_id) INTO 96 BUCKETS;
           

将user_id作為分桶字段,則會根據user_id的值進行哈希分發到桶中。分桶之後具體在使用的時候,有兩種方式:

-- 設定:自動按照分桶表的bucket 進行分桶
SET hive.enforce.bucketing = true
           
-- 設定reduce task為96,并在插入語句中添加CLUSTER BY [filed_name]
SET mapred.reduce.tasks=96
           

警告

對于是以表的中繼資料,指定了分桶并不能保證表可以正确地填充。使用者可根據以上示例來確定是否正确地填充了表。

分桶的幾個優點:

  • 因為桶的數量是固定的,是以它沒有資料波動
  • 友善分桶抽樣
  • 有利于執行高效的map-site JOIN

9.7 為表增加列

使用

ALTER [table_name] ADD COLUMNS (field_name type)

指令,可以在表的末尾添加一個字段。之前缺失新添加的字段的資料,就補null。

實際生産中,随着時間的推移,很有可能會為底層資料增加一個新的字段。

9.8 使用列式存儲

Hive通常使用行式存儲,不過Hive也提供了一個列式SerDe來以混合列式格式存儲資訊。

9.8.1 重複資料

stat uid age
NY Bob 30
NJ Sara 40
NY Peter 14
NY Sandra 5

如果以上資料量很大,那麼state字段與age字段這樣的列會有很多重複資料。這種場景使用列式存儲是非常适用的。例如RCFile采用遊程編碼,相同的資料不會重複存儲,很大程度上節約了存儲空間。

9.8.2 寬表

一般生産中隻會取寬表的部分字段,使用列式存儲,避免整行資料的讀取與過濾。後續會詳細介紹如何使用格式。

另外的:

列式存儲由于相同列的字段類型相同,可以使用高壓縮率的算法對檔案進行壓縮,而行式資料庫由于一行各個字段類型可能不同,在壓縮方式上不容易獲得一個高的壓縮比(也就是空間使用率不高)

列式資料庫的每一列都可以作為索引,而行式資料庫并非所有的字段都适合作為索引。

9.9 (幾乎)總是使用壓縮

幾乎所有的場景都可以使用壓縮,使磁盤上存儲的資料量變小,這樣可以通過降低I/O來提高查詢執行速度。Hive可以無縫使用很多壓縮類型。

唯一不使用壓縮的理由是,資料要直接要提供給外部系統使用,或者使用非壓縮格式(例如文本格式)是最最相容的。

壓縮和解壓縮都會消耗CPU資源。MapReduce任務往往是I/O密集型的,是以CPU開銷往往不是問題。當然對于工作流這樣的CPU密集型的場景,例如一些機器學習算法,壓縮實際上可能會從更多必要的操作中擷取寶貴的CPU資源,進而降低性能。

繼續閱讀