Hive中靜態分割槽和動態分割槽總結
阿新 • • 發佈:2021-03-31
## 目錄
- 背景
- 第一部分 靜態分割槽
- 第二部分 動態分割槽
- 第三部分 兩者的比較
- 第四部分 動態分割槽使用的問題
- 參考文獻及資料
## 背景
在`Hive`中有兩種型別的分割槽:靜態分割槽(Static Partitioning)和動態分割槽(Dynamic Partitioning)。
- **靜態分割槽**。對於靜態分割槽,從字面就可以理解:表的分割槽數量和分割槽值是固定的。
- **動態分割槽**。會根據資料自動的建立新的分割槽。
本文會詳細介紹兩種分割槽方法、使用場景以及生產中常見問題和解決方法。
## 第一部分 靜態分割槽
靜態分割槽的使用場景主要是分割槽的數量是確定的。例如人力資源資訊表中使用“部門”作為分割槽欄位,通常一段時間是靜態不變的。例如:
```sql
CREATE EXTERNAL TABLE employee_dept (
emp_id INT,
emp_name STRING
) PARTITIONED BY (
dept_name STRING
)
location '/user/employee_dept';
LOAD DATA LOCAL INPATH 'hr.txt'
INTO TABLE employee_dept
PARTITION (dept_name='HR');
```
上面的外部表以`dept_name`欄位為分割槽欄位,然後匯入資料需要指定分割槽。
## 第二部分 動態分割槽
通常在生產業務場景中,我們使用的都是靈活的動態分割槽。例如我們使用時間欄位(天、小時)作為分割槽欄位。新的資料寫入會自動根據最新的時間建立分割槽並寫入對應的分割槽。例如下面的例子:
```sql
hive > insert overwrite table order_partition partition (year,month) select order_id, order_date, order_status, substr(order_date,1,4) year, substr(order_date,5,2) month from orders;
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
```
寫入報錯。這是因為Hive預設配置不啟用動態分割槽,需要使用前開啟配置。開啟的方式有兩種:
- 在hive服務配置檔案中全域性配置;
- 每次互動時候進行配置(隻影響本次互動);
通常我們生產環境使用第二種。
```sql
set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nonstrict;
```
其中引數`hive.exec.dynamic.partition.mode`表示動態分割槽的模式。預設是`strict`,表示必須指定至少一個分割槽為靜態分割槽,`nonstrict`模式表示允許所有的分割槽欄位都可以使用動態分割槽。
## 第三部分 兩者的比較
兩種分割槽模式都有各自的使用場景,我們總結如下:
| | 靜態分割槽(Static Partitioning) | 動態分割槽(Dynamic Partitioning) |
| -------- | ------------------------------------------------------ | ------------------------------------------------ |
| 分割槽建立 | 資料插入分割槽之前,需要手動建立每個分割槽 | 根據表的輸入資料動態建立分割槽 |
| 適用場景 | 需要提前知道所有分割槽。適用於分割槽定義得早且數量少的用例 | 有很多分割槽,無法提前預估新分割槽,動態分割槽是合適的 |
另外動態分割槽的值是`MapReduce`任務在`reduce`執行階段確定的,也就是所有的記錄都會`distribute by`,相同欄位(分割槽欄位)的`map`輸出會發到同一個`reduce`節點去處理,如果資料量大,這是一個很弱的執行效能。而靜態分割槽在編譯階段就確定了,不需要`reduce`任務處理。所以如果實際業務場景靜態分割槽能解決的,儘量使用靜態分割槽即可。
## 第四部分 動態分割槽使用的問題
Hive表中分割槽架構使得資料按照分割槽分別儲存在`HDFS`檔案系統的各個目錄中,查詢只要針對指定的目錄集合進行查詢,而不需要全域性查詢,提高查詢效能。
但是分割槽不是"銀彈",如果分割槽資料過多,就會在`HDFS`檔案系統中建立大量的目錄和檔案,對於叢集`NameNode`服務是有效能壓力的,`NameNode`需要將大量元資料資訊保留在記憶體中。另外大分割槽表在使用者查詢時候由於分析`size`太大,也容易造成`Metastore`服務出現`OMM`報錯。
上面兩個現象均在生產環境發生,分別造成`NameNode`和`Metastore`不可用。
事實上,Hive為了防止異常生產大量分割槽,甚至預設動態分割槽是關閉的。另外對於生成動態分割槽的數量也做了效能預設限制。
### 4.1 動態分割槽建立限制
當我們在一個`Mapreduce`任務(`hive`寫入會編譯成`mapreduce`任務)中建立大量分割槽的時候,經常會遇到下面的報錯資訊:
```shell
2015-06-15 17:27:44,614 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
```
這個報錯就是因為Hive對於動態分割槽建立的限制,涉及的引數有:
```sql
hive.exec.max.dynamic.partitions = 1000;
hive.exec.max.dynamic.partitions.pernode = 100;
hive.exec.max.created.files = 10000
```
- `hive.exec.max.dynamic.partitions.pernode`,引數限制`MapReduce`任務單個任務(mapper或reducer任務)建立的分割槽數量為100;
- `hive.exec.max.dynamic.partitions`,引數限制單次整體任務建立分割槽的數量上限為1000個;
- `hive.exec.max.created.files`,引數限制所有單次整體map和reduce任務建立的最大檔案數量上限為10000個;
以上三個閥值超過就會觸發錯誤,叢集會殺死任務。為了解決報錯,我們通常將兩個引數調大。但是也需要使用者對自己的Hive表的分割槽數量進行合理規劃,避免過多的分割槽。
### 4.2 特殊分割槽
如果動態分割槽列輸入的值為NULL或空字串,則Hive將該行將放入一個特殊分割槽,其名稱由引數`hive.exec.default.partition.name`控制。預設值為`__HIVE_DEFAULT_PARTITION__`。
使用者可以使用(查看錶分割槽)命令進行檢視:
```sql
show partitions 'table名稱';
# process_date=20160208
#process_date=__HIVE_DEFAULT_PARTITION__
```
有時候異常生產這些分割槽資料,需要進行清理。如果使用下面的語句:
```sql
ALTER TABLE Table_Name DROP IF EXISTS PARTITION(process_date='__HIVE_DEFAULT_PARTITION__');
```
這時候Hive會報錯:
```shell
Error: Error while compiling statement: FAILED: SemanticException Unexpected unknown partitions for (process_date = null) (state=42000,code=40000)
```
這是Hive一個已知bug(編號:[HIVE-11208](https://issues.apache.org/jira/browse/HIVE-11208)),在`Hive 2.3.0`版本修復。
但是有個有修復方法(不建議在生產環境中實施):
```sql
-- update the column to be "string"
ALTER TABLE test PARTITION COLUMN (p1 string);
-- remove the default partition
ALTER TABLE test DROP PARTITION (p1 = '__HIVE_DEFAULT_PARTITION__');
-- then revert the column back to "int" type
ALTER TABLE test PARTITION COLUMN (p1 int);
```
連結:https://cloudera.ericlin.me/2015/07/how-to-drop-hives-default-partition-__hive_default_partition__-with-int-partition-column/
### 4.3 亂碼分割槽欄位
有時候表分割槽欄位由於處理不當,會出現亂碼分割槽,例如:
```sql
hp_stat_time=r_ready%3D91;r_load%3D351
```
原因是Hive會自動對一些`UTF-8`字元編碼成`Unicode`(類似網址中中文字元和一些特殊字元的編碼處理)。此處`%3D`解碼後是'='。可以使用線上轉換進行解碼:https://www.matools.com/code-convert-utf8。
最後使用解碼後的欄位即可(注意分號轉義):
```sql
alter table dpdw_traffic_base drop partition(hp_stat_time='r_ready=91\;r_load=351');
```
## 參考文獻及資料
1、動態分割槽,連結:https://cwiki.apache.org/confluence/display/Hive/DynamicPartitions
2、Hive Tutorial,連結:https://cwiki.apache.org/confluence/display/Hive/Tutorial
3、Apache Hive 中文手冊,連結:https://www.docs4dev.com/docs/zh/apache-hive/3.1.1/reference
更多關注公眾號:
![image](https://img2020.cnblogs.com/blog/1285229/202103/1285229-20210331125355882-546020