Hive表操作及管理
Hive是基於Hadoop的一個數據倉庫工具,可以將結構化的資料檔案對映為一張資料庫表,並提供簡單的sql查詢功能,可以將sql語句轉換為MapReduce任務進行執行。 其優點是學習成本低,可以通過類SQL語句快速實現簡單的MapReduce統計,不必開發專門的MapReduce應用,十分適合資料倉庫的統計分析。但缺點也非常明顯。因為Hive運算引擎來自MapReduce,MapReduce中間結果都儲存在磁碟,IO導致速度很慢,這也是Hive最顯著的缺點。
作為對元資料SQL化操作的引擎,Hive同樣使用RDBMS的資料表物件管理資料。
一、Hive表
Hive中能存在兩種表:
- 內部表(managed table),資料檔案、統計檔案、元資料都由Hive自己管理,換句話說,這個表資料儲存在哪裡我們不用關心,也不用提供,Hive預設儲存在HDFS。Hive能管理原始資料的整個生命週期。Hive表刪除後,資料也隨之刪除。
- 外部表(external table),資料檔案儲存在其他系統中,可能是HDFS,也可能是HBase、ASV等,HIve只保留對映關係,但Hive表刪除後,資料不會丟失,仍然存在於其他系統中。
二、建立表
由於Hive支援標準的SQL(包括SQL:2003和SQL:2011標準的一些特性),建立表與後面提到的CURD都比較好理解。以下是一些簡單的建立表語句例子:
簡單的內部表
create table tb (id int, name String, age int);
分割槽的內部表
create table ez_part_test ( id int, col1 string, col2 string) partitioned by (type int) stored as orc
外部表(HDFS)
create external table example_csv_tb (
id int,
col1 string,
col2 string) ROW FORMAT
delimited fields terminated by ','
lines terminated by '\n'
stored as textfile location "/ez/example"
HiveSQL不僅僅定義欄位及相應的型別,也提供定義欄位屬性、表儲存格式等的語義。
1、外部表
例如,在HDFS上有個在 hdfs://myhost:8020/ez/example/lang.csv
1,java,20
2,c,40
3,Golang,8
4,Perl,35
5,PHP,20
這時候需要一個Hive表,對映到這個檔案,方便用SQL查詢,這個表既是外部表。同理對映到HBase表也是外部表。於是我們使用如下語句:
create external table lang (
id int,
name string,
field1 int) ROW FORMAT
delimited
fields terminated by ','
lines terminated by '\n'
stored as textfile
location "/ez/example/lang.csv"
檔案第一列是數字,我們定義為id列,第二列定義為name,第三列隨便指定個名字為field。列與列間用逗號隔開。一行記錄結束用換行符 ‘\n’結尾。於是後面的ROW FORMAT row_format子句定義整個格式行為,如下:
row_format
: DELIMITED [FIELDS TERMINATED BY char [ESCAPED BY char]]
[COLLECTION ITEMS TERMINATED BY char]
[MAP KEYS TERMINATED BY char]
[LINES TERMINATED BY char]
[NULL DEFINED AS char] -- (Note: Available in Hive 0.13 and later)
| SERDE serde_name [WITH SERDEPROPERTIES (property_name=property_value, property_name=property_value, ...)]
必須定義的兩個屬性之一,其一是DELIMITED,其二是SERDE。第一個用於描述文字檔案,第二個用於描述二進位制檔案(序列化與反序列化)。我們只涉及到描述文字檔案。於是描述欄位間的分割符號為',',行結束符為'\n'。
stored as file_format宣告儲存的檔案為什麼格式。
file_format:
: SEQUENCEFILE
| TEXTFILE -- (Default, depending on hive.default.fileformat configuration)
| RCFILE -- (Note: Available in Hive 0.6.0 and later)
| ORC -- (Note: Available in Hive 0.11.0 and later)
| PARQUET -- (Note: Available in Hive 0.13.0 and later)
| AVRO -- (Note: Available in Hive 0.14.0 and later)
| INPUTFORMAT input_format_classname OUTPUTFORMAT output_format_classname
這裡我們配置TEXTFILE(文字檔案),也是預設的Hive外部表儲存格式。
location hdfs_path子句意思很明顯了,資料檔案儲存的位置,注:必須是HDFS檔案路徑。
至此一個最簡單的外部表就建立完成了。可以直接使用如下語句查詢試試:
select * from lang;
注意:這裡我們沒有指定資料庫,所以這個表將建立在default資料庫中,Hive預設內建的一個數據庫。
2、內部表
例如,我們沒有資料檔案,只是想建立一個Hive表,這個Hive表以後用於儲存資料用,或從其他資料來源匯入,或程式寫入等。內部表看起來就像RDBMS的資料表,Hive有足夠大的許可權可以操縱表裡的資料,包括刪除。
建立外部表時,使用的語句是create external table。而預設情況下,不使用external則是預設建立內部表。
以下是建立表語法:
CREATE [TEMPORARY] [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name -- (Note: TEMPORARY available in Hive 0.14.0 and later)
[(col_name data_type [COMMENT col_comment], ... [constraint_specification])]
[COMMENT table_comment]
[PARTITIONED BY (col_name data_type [COMMENT col_comment], ...)]
[CLUSTERED BY (col_name, col_name, ...) [SORTED BY (col_name [ASC|DESC], ...)] INTO num_buckets BUCKETS]
[SKEWED BY (col_name, col_name, ...) -- (Note: Available in Hive 0.10.0 and later)]
ON ((col_value, col_value, ...), (col_value, col_value, ...), ...)
[STORED AS DIRECTORIES]
[
[ROW FORMAT row_format]
[STORED AS file_format]
| STORED BY 'storage.handler.class.name' [WITH SERDEPROPERTIES (...)] -- (Note: Available in Hive 0.6.0 and later)
]
[LOCATION hdfs_path]
[TBLPROPERTIES (property_name=property_value, ...)] -- (Note: Available in Hive 0.6.0 and later)
[AS select_statement]; -- (Note: Available in Hive 0.5.0 and later; not supported for external tables)
CREATE [TEMPORARY] [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name
LIKE existing_table_or_view_name
[LOCATION hdfs_path];
data_type
: primitive_type
| array_type
| map_type
| struct_type
| union_type -- (Note: Available in Hive 0.7.0 and later)
primitive_type
: TINYINT
| SMALLINT
| INT
| BIGINT
| BOOLEAN
| FLOAT
| DOUBLE
| DOUBLE PRECISION -- (Note: Available in Hive 2.2.0 and later)
| STRING
| BINARY -- (Note: Available in Hive 0.8.0 and later)
| TIMESTAMP -- (Note: Available in Hive 0.8.0 and later)
| DECIMAL -- (Note: Available in Hive 0.11.0 and later)
| DECIMAL(precision, scale) -- (Note: Available in Hive 0.13.0 and later)
| DATE -- (Note: Available in Hive 0.12.0 and later)
| VARCHAR -- (Note: Available in Hive 0.12.0 and later)
| CHAR -- (Note: Available in Hive 0.13.0 and later)
array_type
: ARRAY < data_type >
map_type
: MAP < primitive_type, data_type >
struct_type
: STRUCT < col_name : data_type [COMMENT col_comment], ...>
union_type
: UNIONTYPE < data_type, data_type, ... > -- (Note: Available in Hive 0.7.0 and later)
row_format
: DELIMITED [FIELDS TERMINATED BY char [ESCAPED BY char]] [COLLECTION ITEMS TERMINATED BY char]
[MAP KEYS TERMINATED BY char] [LINES TERMINATED BY char]
[NULL DEFINED AS char] -- (Note: Available in Hive 0.13 and later)
| SERDE serde_name [WITH SERDEPROPERTIES (property_name=property_value, property_name=property_value, ...)]
file_format:
: SEQUENCEFILE
| TEXTFILE -- (Default, depending on hive.default.fileformat configuration)
| RCFILE -- (Note: Available in Hive 0.6.0 and later)
| ORC -- (Note: Available in Hive 0.11.0 and later)
| PARQUET -- (Note: Available in Hive 0.13.0 and later)
| AVRO -- (Note: Available in Hive 0.14.0 and later)
| INPUTFORMAT input_format_classname OUTPUTFORMAT output_format_classname
constraint_specification:
: [, PRIMARY KEY (col_name, ...) DISABLE NOVALIDATE ]
[, CONSTRAINT constraint_name FOREIGN KEY (col_name, ...) REFERENCES table_name(col_name, ...) DISABLE NOVALIDATE
三、建立分割槽表
目前我只嘗試過在內部表上分割槽,沒有嘗試過外部表分割槽。分割槽是將HDFS中不同分割槽的資料檔案存放於不同的目錄下。例如一個Hive內部表資料保存於HDFS中/user/hive/warehouse/mytest.db/下,這個目錄下全是資料檔案(可能是文字檔案格式,也可能是其他格式,根據file_format指定)。這時候若引入一個欄位用於分割槽,則將會在這個目錄下產生新的目錄,每個目錄對應一個分割槽。每個分割槽資料分別存在自己對應的目錄下。這樣查詢效率也會更高。
建立分割槽表將使用到partitioned by子句。如下分割槽表:
create table partition_tb (
id int, col1 string, col2 string)
partitioned by (type int)
stored as ORC
這裡需要注意,當指定分割槽列時,分割槽列不能是資料列。上面type列就沒有出現在資料列(id,col1,col2)中。而在查詢和寫入語句中,type列完全可以像普通資料列一樣使用,放在where子句,放在select列中。
當表建立好後,只是聲明瞭有一個叫type的列可以用於分隔資料,但沒有建立分割槽出來,也就是說,現在HDFS目錄下還沒有分割槽目錄,分割槽資訊的元資料也沒有形成。那我們是等資料寫入時再檢測是否分割槽存在,並建立與否,還是事先建立好所有的分割槽,靜等資料進入呢?這個也是Hive靜態分割槽與動態分割槽的問題。我們完全可以配置使Hive允許動態分割槽。這裡我只記錄了非動態分割槽的做法(後續學習了動態分割槽留待補充)。配置引數hive.exec.dynamic.partition.mode=nonstrict
。
ALTER TABLE table_name ADD [IF NOT EXISTS] PARTITION partition_spec
[LOCATION 'location1'] partition_spec [LOCATION 'location2'] ...;
partition_spec:
: (partition_column = partition_col_value, partition_column = partition_col_value, ...)
結合上面的partition_tb表,我們建立一個分割槽:
alter table partition_tb add partition (type = 1)
注意:這裡也可以指定LOCATION子句,這樣分割槽目錄將建立到LOCATION下。但之前的資料將還存在這個表中。
為表做分割槽主要考慮的是效率問題,重點需要考慮分割槽的列的基數(也就是該列包含唯一值的個數)。選擇基數很高的列來做分割槽會導致資料嚴重的碎片化。不要對資料過分的分割槽。如果有太多的小分割槽,那麼對這麼多的分割槽目錄進行掃描代價也是比較高的,甚至可能比全表掃描還高。
四、刪除表
上面已經提到,對於內部表,刪除後hive也會刪除HDFS上儲存的資料。對於外部表,只是表的元資料(metastore)會刪除,真實資料並不會刪除。
DROP TABLE [IF EXISTS] table_name [PURGE]; -- (Note: PURGE available in Hive 0.14.0 and later)
下一篇將記錄表操作CURD及筆者倒騰Hive遇到的一些坑。