天天看點

Hive中内部表、外部表、分區、分桶以及SQL的執行順序

在Hive中有四種資料模型——内部表、外部表、分區表、桶,下面一一介紹下這四種不同的模型。

Hive内部表和外部表

内部表和外部表最直覺的區分其實是通過是否使用external關鍵詞進行修飾,被external修飾的為外部表。内部表和外部表具有以下三個主要的不同點:

1) 是否被關鍵詞external修飾。

2) 删除外部表、中繼資料會被删除,但是實際的資料不會真正的删除,還是會存在指定的位置,而内部表的删除,中繼資料和資料都會被删除。

3)在導入資料到外部表時,資料并沒有移動到資料倉庫(/user/hive/warehouse)目錄下,且資料是由HDFS管理。而内部表的資料都會存儲在(/user/hive/warehouse)目錄下,且資料是由Hive自身管理。

兩者的使用場景也是有所差別的。

外部表使用場景——導入HDFS的資料,可以使用者存儲一些日志資訊,但是資料不會被删除。

内部表使用場景——用于存放Hive處理的中間表、結果表,邏輯處理的中間過程生成的中間表,或者一些臨時表,可以使用完後直接删除。

在實際應用過程中,通常是外部和内部表一起使用。例如将每日的日志資料傳入HDFS中,一天一個目錄,Hive基于流入的資料建立外部表,将每天HDFS上的原始日志映射到外部表的每天分區中;然後再外部表的基礎上做統計分析,使用内部存儲中間表、結果表,通過SELECT+外部表+INSERT 進入内部表。

在Hive中建表的方式有三種:直接建表、抽取建表、Like建表。分别用案列解釋下這幾種建表方式

  • 直接建表

直接建表是可以直接進行字段類型,字段備注,資料存儲格式自定義等。

-- 建立内部表
create table article_internal(
    sentence string
)
row format delimited fields terminated by '\t'  -- 字元之間的分割符
lines terminated by '\n'; -- 每一行之間的分割符

-- 建立外部表
create external table article_external(
    sentence string
) 
row format delimited fields terminated by '\t' -- 字元之間的分割符
lines terminated by '\n'; -- 每一行之間的分割符

load data local inpath '/usr/local/src/code/hive_data/The_Man_of_Property.txt'
overwrite into table article_external -- 導入本地資料到指定的表

load data local inpath '/usr/local/src/code/hive_data/The_Man_of_Property.txt'
overwrite into table article_internal -- 導入本地資料到指定表
           

在上面的代碼中展示了内部表和外部表的建表語句,我們也可以直接導入資料到指定的表中去,上面是指定的本地資料,如果是想将HDFS上的資料導入到表中,則隻需要先将檔案上傳到HDFS,然後執行上面的load data語句隻是去除local關鍵詞,就會将HDFS上的資料導入到指定表中。

我們也通過這個案例區分一下内部表和外部表之間的不同之處。首先,檢視表結構(show create table xxx)。可以看出其實最大的差別就是外部表多一個external關鍵詞,其他的都是類似。

Hive中内部表、外部表、分區、分桶以及SQL的執行順序

其次。驗證内部表和外部表的删除。在導入表的資料之後,會發現無論是内部表還是外部表,其實都會把資料上傳到HDFS上。Hive表的删除分為删除資料(保留表)、删除表。删除資料使用truncate table 表名,但是這種隻能删除内部表的資料,因為外部表的資料并不是存放在Hive中繼資料存儲中;删除表用Drop table 表名。這裡就實踐一下drop删除表來檢視内部表和外部表的不同。

Hive中内部表、外部表、分區、分桶以及SQL的執行順序

删除之前HDFS存儲的資料

内部表drop之後,會看到表結構和HDFS上的資料都不存在,

Hive中内部表、外部表、分區、分桶以及SQL的執行順序

外部表删除後,中繼資料被删除,但是在HDFS上的資料依然存在。

Hive中内部表、外部表、分區、分桶以及SQL的執行順序
  • 抽取建表——as

抽取(as)建表的使用場景常見于中間邏輯處理的時候,進行建表,直接複制表的資料和結構時。

create table article_as as select * from artilce;
           
  • like建表

Like建表适用于隻關注表結構,不需要資料。

create table article_like like article
           

分區

為什麼要引入分區的概念?這是因為在單個表資料量越來越大時,Hive Select查詢一般會掃描整個表的内容,會消耗很多的時間做沒必要的工作,有時候隻需要掃描表中最關心的一部分資料,是以在建表的時候引入partition的概念,用于減少查詢的資料量,提供查詢的執行效率。你不禁會問為什麼不使用索引?索引難道不能實作嗎?其實,在Hive中也是支援索引的,但是和分區還是有很大的差別,兩者的差別點在于——索引不分割資料庫,分區會分割資料庫,索引其實就是拿額外的存儲空間換查詢時間,但是分區已經将整個大資料按照分區列拆分成了多個小的資料庫。

那改如何分區?一般根據業務需求來進行分區,完全看業務場景,常用的是用年、月、日、男女性别、年齡段或者是能夠平均将資料分到不同檔案中的屬性。選擇合适的分區是十分重要的,因為分區不好将會直接導緻查詢結果延遲。在工作中常用日的次元來進行分區,用d或者dt字段表示,一般将今天當做t,分區的資料一般是t-1。

對于分區有以下五點小的細節之處:

1)一個表可以擁有一個或者多個分區,每個分區以檔案夾的形式單獨存在表檔案夾的目錄下。

2)表和列名不區分大小寫。

3) 分區是以字段的形式在表結構中存在,通過desc table指令可以檢視到字段存在(算是僞列),該字段實際上并不存放在實際資料内容中,僅僅是分區表示。

4) 分區有一級、二級設定,一般設定為一級分區。

5)分區也分為動态分區和靜态分區。

上面的五點其實前四點很好了解,對于第五點其實比較疑惑,什麼是動态?什麼是靜态?其實用下面的兩個insert 語句就可以很好的區分兩者的差別。

-- 靜态分區插入資料
insert overwrite table udata_paratition partition(dt='2020-12-19')  select user_id,item_id,rating from udata  where user_id='305'

-- 動态分區插入資料
insert overwrite table udata_paratition partition(dt) select user_id,item_id,rating,to_date(from_unixtime(cast('timestamp' as bigint),'yyyy-MM-dd HH:mm:ss')) res  from udata  where user_id='244'

           

很明顯,Hive的靜态分區,就是要手動指定分區的值為靜态值,這種對于小批量的分區插入比較友好,但是大量的導入就顯得力不從心了。是以動态分區就是分區值設定為動态的值,動态分區你不指定查詢結果字段值時,會預設最後一列的值作為分區值,或者手動設定查詢結果的哪一列。

insert overwrite table udata_partition(dt=res) select  to_date(from_unixtime(cast('timestamp' as bigint),'yyyy-MM-dd HH:mm:ss')) res from udata 
           

分區的數量需要根據業務需求嚴格規劃,因為分區會占用IO資源,數量越多,IO資源消耗越大,查詢時間和性能都是有損耗的。另外動态分區是預設是關閉的,是以如果使用動态分區之前需要手動打開動态分區。在實際工作中趨向于使用動态分區,而且對于前端日志埋點的資料,一般都會采取day+hour的形式作為分區(dt='2020-12-19'/hour='01')。

-- 打開動态分區模式
set hive.exec.dynamic.partition = true;
-- 設定分區模式為非嚴格模式
set hive.exec.dynamic.partition.mode=nonstrict;
-- 設定一條帶有動态分區SQL語句所能建立的最大動态分區總數,超過則報錯
set hive.exec.dynamic.partitions=10;
           

下面是分區的一些基本操作:

Hive中内部表、外部表、分區、分桶以及SQL的執行順序

建立分區

Hive中内部表、外部表、分區、分桶以及SQL的執行順序

檢視分區表結構和分區資料

 對于分區的以下操作指令一般是比較常用的:

--檢視表結構

show create table udata_partition;

desc udata_partition【可以看到分區資訊】

-- 檢視分區表分區

show partitions udata_paratition;
           

另外當表中有資料之後,在HDFS上會對應有生成分區目錄。如下圖所示:

Hive中内部表、外部表、分區、分桶以及SQL的執行順序

分桶

為了将Hive中的資料進行不同程度上的劃分,進而讓查詢在較小的範圍資料上提高查詢效率,Hive中提出了分區以及分桶的兩種政策,分區是粗粒度的劃分,分桶是可以更進一步的細粒度的劃分。Hive才用對列值哈希,然後除以桶的個數求餘的方式決定該條記錄放在哪個桶中,可以了解成MapReduce中的HashPartitioner,兩者都是基于Hash對資料進行分桶。

以下是分桶的一個計算過程。

分桶計算:hive計算桶列的hash值再除以桶的個數取模,得到某條記錄到底屬于哪個桶。

定義桶數:3個 0 1 2

輸入以下資料:

    user_id    order_id    gender

    196         12010200       1

    186         19201000        0

    22           12891999        1

    244          19192908        0

計算:

    196%3=1 ,放入1号桶

    186%3=0,放入0号桶

    22%3=1,放入1号桶

    244%3=1,放入1号桶
           

分桶常用的應用場景包括:資料抽樣、Map-Side Join。

Map-Side Join:可以獲得更高的查詢處理效率。桶為了表加上額外的結構,(利用原有字段進行分桶),Hive在處理有些查詢時能利用這個結構。具體而言,連接配接兩個在(包含連接配接列)相同列上劃分了同的表,可以使用Map端連接配接(Map-side join)高效的實作。比如JOIN操作,對于JOIN操作兩個表有一個相同的列,如果對這兩個表都進行桶操作。那麼将儲存相同列值的桶進行JOIN操作就可以,可以大大減少JOIN的資料量。

資料抽樣:在處理大規模資料集時,尤其資料挖掘階段,可以用一份資料驗證一下,代碼是否可以運作成功,進行局部測試,也可以抽樣進行一些代表性統計分析。具體如何進行資料抽樣,見下面的SQL。

select * from student tablesample(bucket 1 out of 32 on id)
           

tablesample是抽樣語句,文法為:Tablesample(Bucket x OUT OF y);

-x表示從第幾個分桶進行抽樣,y表示每隔幾個分桶取一個分桶,y必須是table總bucket數的倍數或者因子。hive根據y的大小,決定抽樣比列。例如,table總共分為64份,當y=32時,抽取(64/32)2個bucket的資料,當y=128時,抽取(64/128)0.5個bucket資料。x表示從哪個bucket抽取。當table總Bucket數為32,tablesmaple(bucket 1 out of 16),表示總共抽取2個bucke資料,分别為第1個和第(1+16)17個bucket的資料。

Bucket和動态分區一樣,在最開始是預設關閉的,是以需要我們手動去設定為啟用——set hive.enforce.bucketing = true。

下面是關于分桶的基本操作:

-- 建立分桶
create table bucket_user(
    id int
)clustered by(id) into 4 buckets;

-- 導入資料
insert overwrite table bucket_user select cast(user_id as int) from udata;
           
Hive中内部表、外部表、分區、分桶以及SQL的執行順序

有了分區和分桶的概念,那什麼時候使用分區?什麼時候使用分桶呢?資料量比較大的時候,為了快速查詢使用分區;更加細粒度的查詢、資料抽樣或者資料傾斜使用分桶。

Hive分桶的概念就是MapReduce的分區概念,兩者完全相同。實體上每個桶就是目錄裡的一個檔案,一個作業産生的桶(輸出檔案)數量和Reduce任務個數是相同的。而分區表的概念,則是新的概念。分區代表了資料的倉庫,也就是檔案夾目錄。每個檔案夾下面可以放不同的資料檔案。通過檔案夾可以查詢裡面存放的檔案。但檔案夾本身和資料内容毫無關系。桶則是按照資料内容的某個值進行分桶,把一個大檔案散列成為一個個小檔案。這些小檔案可以單獨排序。如果另外一個表也按照同樣的規則分成了一個個小檔案。兩個表Join的時候,就不必要掃描整個表,隻需要比對相同分桶的資料即可,大大的提升了效率。同樣對資料抽樣的時候,也不需要掃描整個檔案,隻需要對每個分區按照相同規則抽取一部分資料即可。

Hive表的執行順序

在Hive中執行語句的SQL一般書寫格式為:select .... from .... where ... group by ... having...order by...

它的執行順序為:from...where...select...group by...having...order by..

執行順序其實可以分為兩個部分,剛好對應MapReduce的兩個階段,Map階段:from...where...select,剩餘則是在Redcue端進行執行。

  • Map階段:

① 執行from加載,進行表的查找與加載

② 執行where過濾,進行條件過濾與篩選

③ 執行select查詢,進行輸出項的篩選

④ map端檔案合并:map端本地溢寫檔案的合并操作,每個map最終形成一個臨時檔案。然後按列映射到對應的reduce。

  • Reduce階段:

① group by :對map端發送過來的資料進行分組并進行計算。

② having:最後過濾列用于輸出結果

③ order by :排序後進行結果輸出到HDFS檔案。

繼續閱讀