面試官問我,什麼是hive的靜態分割區和動態分割區,這題我會呀。
分割區是hive存放資料的一種方式,將列值作為目錄來存放資料,就是一個分割區,可以有多列。
這樣查詢時使用分割區列進行過濾,只需根據列值直接掃描對應目錄下的資料,不掃描不關心的分割區,快速定位,提高查詢效率。
hive的分割區有兩種型別:
對於靜態分割區,表的分割區數量和分割區值是固定的。新增分割區或者是載入分割區資料時,需要提前指定分割區名。
對於動態分割區,分割區的值是不確定的,會根據資料自動的建立新的分割區。
如上所述,靜態分割區的使用場景主要是分割區的數量是確定的。例如紀錄檔流水資料中使用日期作為分割區欄位,通常在寫入之前就已經確定了是哪個分割區。
create table if not exists day_log(
uid bigint,
uname string,
action string
) comment '使用者動作流水記錄'
partitioned by(ymd string comment '日期格式yyyyMMdd')
row format delimited fields terminated by '\t';
load data local inpath '/user/hive/data/day_log.txt'
into table day_log paritition(ymd='20220803')
create table if not exists day_log(
uid bigint,
uname string,
action string
) comment '使用者動作流水記錄'
partitioned by(year string,month string,day string)
row format delimited fields terminated by '\t';
load data local inpath '/user/hive/data/day_log.txt'
into table day_log paritition(year='2022',month='08',day='02')
但通常我們寫入分割區資料是通過計算SQL結果直接寫入,並不是從外部檔案load進來的。範例如下:
insert overwrite table day_log partition (year='2022',month='08',day='02')
select uid,uname,action from (
xxxxxx
)
所謂動態分割區,分割區的值是不確定的,分割區的數量是不確定,皆由載入資料確定。
生產環境中,動態分割區一般常用於建立新表後,需要一次性載入歷史資料。
-- 建立臨時表
create table if not exists tmp (
uid int,
commentid bigint,
recommentid bigint,
year int,
month int,
day int
)
row format delimited fields terminated by '\t';
-- 載入資料到臨時表
load data local inpath 'user/hive/data/tmp.txt' into table tmp;
-- 建立動態分割區表
create table if not exists dp_tmp(
uid int,
commentid bigint,
recommentid bigint
)
partitioned by (year string,month string,day string)
row format delimited fields terminated by '\t';
-- 寫入資料到分割區表
-- 引數為開啟動態分割區
set hive.exec.dynamic.partition=true;
insert overwrite table dp_tmp partition(year,month,day)
select uid,commentid,recommentid,year,month,day from tmp;
執行上述寫入語句會報錯:
FAILED: SemanticException [Error 10096]: Dynamic partition strict mode requires at least one static partition column. To turn this off set hive.exec.dynamic.partition.mode=nonstrict
看報錯資訊:動態分割區嚴格模式至少需要一個靜態分割區列。關閉它,設定引數
set hive.exec.dynamic.partition.mode=nonstrict
下文介紹hive相關引數作用
引數hive.exec.dynamic.partition.mode
表示動態分割區的模式。
預設是strict
,也就是嚴格模式,表示必須指定至少一個分割區為靜態分割區
nonstrict
模式,即非嚴格模式,表示允許所有的分割區欄位都可以使用動態分割區
嚴格模式
-- 至少需要指定一個靜態分割區列
-- 開啟動態分割區
set hive.exec.dynamic.partition=true;
insert overwrite table dp_tmp partition(year='2022',month,day)
select uid,commentid,recommentid,month,day from tmp;
set hive.exec.dynamic.partition=true;
-- 允許所有的分割區欄位都可以使用動態分割區,相容嚴格模式
-- 更改動態分割區模式為非嚴格模式
set hive.exec.dynamic.partition.mode=nonstrict;
insert overwrite table dp_tmp partition(year,month,day)
select uid,commentid,recommentid,month,day from tmp;
通常情況下,我們使用動態分割區,為非嚴格模式:
set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nonstrict;
兩種分割區模式根據定義就可看出來明顯區別,這裡單列一下:
靜態分割區(Static Partitioning) | 動態分割區(Dynamic Partitioning) | |
---|---|---|
分割區建立 | 資料插入分割區之前,需要手動指定建立每個分割區 | 根據表的輸入資料動態建立分割區 |
適用場景 | 需要提前知道所有分割區。適用於分割區定義得早且數量少的用例,常見為插入某一個指定分割區 | 有很多分割區,無法提前預估新分割區,動態分割區是合適的 |
另外動態分割區的值是MapReduce
任務在reduce
執行階段確定的,也就是所有的記錄都會distribute by
,相同欄位(分割區欄位)的map
輸出會發到同一個reduce
節點去處理,如果資料量大,這是一個很弱的執行效能。
而靜態分割區在編譯階段就確定了,不需要reduce
任務處理。所以如果實際業務場景靜態分割區能解決的,儘量使用靜態分割區即可。
hive表中的分割區作用主要是使資料按照分割區目錄儲存在hdfs上,查詢只要針對指定的目錄集合進行查詢,避免全域性查詢,這樣提高了查詢效能。
hive的分割區需要合理使用,過多的分割區目錄和檔案對於叢集Namenode
服務是有效能壓力的,Namenode
需要將大量的後設資料資訊儲存在記憶體中。如果報錯,會造成Namenode
不可用。
一次查詢表裡有太多分割區,會使得查詢檔案過大,也會造成Metastore
服務出現OOM
報錯,報錯資訊顯示Metastore
不可用。
hive為了避免因為異常產生大量分割區,導致上述問題,本身是預設動態分割區關閉,同時對生成動態分割區的數量也做了一定限制。
通過手動引數設定可以改變系統預設值,具體hive預設引數以及SQL執行設定引數(不同版本預設引數有一定差異)如下:
-- Hive預設設定值
-- 開啟或關閉動態分割區
hive.exec.dynamic.partition=false;
-- 設定為nonstrict模式,讓所有分割區都動態設定,否則至少需要指定一個分割區值
hive.exec.dynamic.partition.mode=strict;
-- 能被mapper或reducer建立的最大動態分割區數,超出而報錯
hive.exec.max.dynamic.partitions.pernode=100;
-- 一條帶有動態分割區SQL語句所能建立的最大動態分割區總數,超過則報錯
hive.exec.max.dynamic.partitions=1000;
-- 全域效能被建立檔案數目的最大值,通過Hadoop計數器跟蹤,若超過則報錯
hive.exec.max.created.files=100000;
-- 根據個人需要設定
-- 設定動態分割區開啟
set hive.exec.dynamic.partition=true;
-- 設定為非嚴格模式
set hive.exec.dynamic.partition.mode=nonstrict;
-- 設定每個節點建立最大分割區數
set hive.exec.max.dynamic.partitions.pernode=1000;
-- 設定執行SQL建立最大分割區數
set hive.exec.max.dynamic.partitions=10000;
-- 設定全域性被建立檔案最大值
set hive.exec.max.created.files=1000000;
在執行hiveSQL的時候如果動態分割區數量或檔案數任何一個超過叢集預設就會產生報錯:
ERROR [LocalJobRunner Map Task Executor #0]:mr.ExecMapper (ExecMapper.java:map(171)) - org.apache.hadoop.hive.ql.metadata.HiveException: Hive Runtime Error while processing row ....
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... 10 more
叢集會kill任務。為了解決報錯,我們通常將三個引數調大。但是也需要使用者對自己的Hive表的分割區數量進行合理規劃,避免過多的分割區。
a. 儘量不要使用動態分割區,因為動態分割區的時候,將會為每一個分割區分配reducer數量,當分割區數量多的時候,reducer數量將會增加,對伺服器是一種災難。
b. 動態分割區和靜態分割區的區別,靜態分割區不管有沒有資料都會建立指定分割區,動態分割區是有結果集將建立,否則不建立。
c. hive動態分割區的嚴格模式和hive嚴格模式是不同的。
hive提供的嚴格模式簡述:
hive提供的嚴格模式,為了組織使用者不小心提交惡意SQL
hive.mapred.mode=nostrict : strict
如果該模式值為strict,將會阻止一下三種查詢:
a.對分割區表查詢,where條件中過濾欄位沒有分割區欄位;
b.笛卡爾積join查詢,join查詢語句中不帶on條件或者where條件;
c.對order by查詢,有order by的查詢不太limit語句。
a.預設分割區
如果動態分割區列輸入的值為NULL或空字串,則hive將該行放入一個特殊分割區,分割區名稱由引數hive.exec.default.partition.name
控制。
預設值為__HIVE_DEFAULT_PARTITION__
。可以通過檢視表分割區命令進行檢視:
show partitions 'table';
-- ymd=__HIVE_DEFAULT_PARTITION__
清理該分割區使用正常刪除分割區語句即可。對分割區的操作命令詳見上篇文章。
b.亂碼分割區
表分割區欄位處理不當可能會造成亂碼分割區,主要是由於轉譯編碼原因造成。例如:
sp_test=r_ready%3D91;r_load%3D351
原因是Hive會自動對一些UTF-8字元編碼成Unicode(類似網址中中文字元和一些特殊字元的編碼處理)。此處%3D解碼後是'='。可以使用線上轉換進行解碼:https://www.matools.com/code-convert-utf8。
最後使用解碼後的欄位即可(注意分號跳脫):
alter table dpdw_traffic_base drop partition(sp_test='r_ready=91\;r_load=351');
按例,我的個人公眾號:魯邊社,歡迎關注
後臺回覆關鍵字 hive,隨機贈送一本魯邊備註版珍藏巨量資料書籍。