Hive Tutorial
- Hive Tutorial
- Concepts
- What Is Hive
- What Hive Is NOT
- Getting Started
- Data Units
- Type System
- Built In Operators and Functions
- Language Capabilities
- Usage and Examples
- Creating, Showing, Altering, and Dropping Tables
- Loading Data
- Querying and Inserting Data
概念
Hive是什麼
Hive是一個基於Apache Hadoop的數據倉庫。對於數據存儲與處理,Hadoop提供了主要的擴展和容錯能力。
Hive設計的初衷是:對於大量的數據,使得數據彙總,查詢和分析更加簡單。它提供了SQL,允許用戶更加簡單地進行查詢,彙總和數據分析。同時,Hive的SQL給予了用戶多種方式來集成自己的功能,然後做定製化的查詢,例如用戶自定義函數(User Defined Functions,UDFs).
Hive不適合做什麼
Hive不是爲在線事務處理而設計。它最適合用於傳統的數據倉庫任務。
Getting Started
對於Hive, HiveServer2 和Beeline的設置詳情,請參考指南。
對於學習Hive,這些書單可能對你有所用處。
以下部分提供了一個關於Hive系統功能的教程。先描述數據類型,表和分區的概念,然後使用例子來描述Hive的功能。
數據單元
根據顆粒度的順序,Hive數據被組織成:
- 數據庫(Databases):命名空間功能,爲了避免表,視圖,分區,列等等的命名衝突。數據庫也可以用於加強用戶或用戶組的安全。
-
表(Tables):相同數據庫的同類數據單元。例如表
page_views
,表的每行包含以下列:- timestamp -這是一個
INT
類型,是當頁面被訪問時的UNIX時間戳。 - userid -這是一個
BIGINT
類型,用於惟一識別訪問頁面的用戶。 - page_url -這是一個
STRING
類型,用於存儲頁面地址。 - referer_url -這是一個
STRING
類型,用於存儲用戶是從哪個頁面跳轉到本頁面的地址。 - IP -這是一個
STRING
類型,用於存儲頁面請求的IP地址。
- timestamp -這是一個
-
分區(Partitions):每個表可以有一個或多個用於決定數據如何存儲的分區鍵。分區(除存儲單元之外)也允許用戶有效地識別滿足指定條件的行;例如,
STRING
類型的date_partition
和STRING
的country_partition
。這些分區鍵的每個惟一的值定義了表的一個分區。例如,所有的“2009-12-23”日期的“US”數據是表page_views
的一個分區。(注意區分,分區鍵與分區,如果分區鍵有兩個,每個分區鍵有三個不同的值,則共有6個分區)。因此,如果你只在日期爲“2009-12-23”的“US”數據上執行分析,你將只會在表的相關數據上執行查詢,這將有效地加速分析。然而要注意,那僅僅是因爲有個分區叫2009-12-23,並不意味着它包含了所有數據,或者說,這些數據僅僅是那個日期的數據。用戶需要保證分區名字與數據內容之間的關係。分區列是虛擬列,它們不是數據本身的一部分,而是源於數據加載。 -
桶(Buckets or Clusters):每個分區的數據,基於表的一些列的哈希函數值,又被分割成桶。例如,表
page_views
可能通過userid
分成桶,userid
是表page_view
的一個列,不同於分區列。這些桶可以被用於有效地抽樣數據。
注意,不需要對錶進行分區或套接,但是這些抽象允許系統在查詢處理過程中刪除大量數據,從而導致查詢執行速度更快。
類型系統
Hive支持原始類型和復要類型,如下所述,查看Hive Data Types
原始類型
- 類型與表的列相關。支持以下原始類型:
- Integers(整型)
- TINYINT -1位的整型
- SMALLINT -2位的整型
- INT -4位的整型
- BIGINT -8位的整型
- 布爾類型
- BOOLEAN -TRUE/FALSE
- 浮點數
- FLOAT -單精度
- DOUBLE -雙精度
- 定點數
-DECIMAL -用戶可以指定範圍和小數點位數 - 字符串
-STRING -在特定的字符集中的一個字符串序列
-VARCHAR -在特定的字符集中的一個有最大長度限制的字符串序列
-CHAR -在特定的字符集中的一個指定長度的字符串序列 - 日期和時間
-TIMESTAMP -一個特定的時間點,精確到納秒。
-DATE -一個日期 - 二進制
-BINARY -一個二進制位序列
複雜類型
複雜類型可以由原始類型和其他組合類型構建:
- 結構體類型(Stuct): 使用點(.)來訪問類型內部的元素。例如,有一列c
,它是一個結構體類型{a INT; b INT},字段a可以使用表達式c.a
來訪問。
- Map(key-value鍵值對):使用['元素名']
來訪問元素。例如,有一個MapM
,包含'group'->gid
的映射,則gid的值可以使用M['group']
來訪問。
- 數組:數組中的元素是相同的類型。可以使用[n]
來訪問數組元素,n
是數組下標,以0開始。例如有一個數組A,有元素['a','b','c']
,則A[1]
返回'b'
。
內置運算符和函數
下面列出的操作符和函數不一定是最新的。(Hive運營商和udf有更多的當前信息。)在Beeline或Hive CLI中,使用這些命令來顯示最新的文檔:
SHOW FUNCTIONS;
DESCRIBE FUNCTION <function_name>;
DESCRIBE FUNCTION EXTENDED <function_name>;
注意:Hive所有關鍵詞的大小寫都不敏感,包括Hive運算符和函數的名字。
內置運算
- 關係運算
- 數學運算
- 邏輯運算
- 複雜類型的運算
內置函數
- Hive支持以下內置函數
- Hive支持以下內置聚合函數
語言能力
Hive’s SQL提供了基本的SQL操作。這些操作工作於表或分區上。這些操作是:
- 可以使用WHERE
從表中篩選行
- 可以使用SELECT
從表中查詢指定的列
- 兩個表之間可以join
- 可以在多個group by
的列上使用聚合
- 可以存儲查詢結構到另一個表
- 可以下載表的內容到本地目錄
- 可以存儲查詢結果到hadoop的分佈式文件系統目錄上
- 可以管理表和分區(創建,刪除和修改)
- 可以使用自定義的腳本,來定製map/reduce作業
使用和實例
以下的例子有一些不是最新的,更多最新的信息,可以參考LanguageManual。
以下的例子強調了Hive系統的顯著特徵。詳細的查詢測試用例和相應的查詢結果可以在Hive Query Test Cases’上找到。
- 創建,顯示,修改,和刪除表
- 加載數據
- 查詢和插入數據
創建,顯示,修改,和刪除表
創建表
以下例子創建表page_view
:
- 1
- 2
- 3
- 4
- 5
- 6
在這個例子中,表的列被指定相應的類型。備註(Comments)可以基於列級別,也可以是表級別。另外,使用PARTITIONED
關鍵詞定義的分區列與數據列是不同的,分區列實際上不存儲數據。當使用這種方式創建表的時候,我們假設數據文件的內容,字段之間以ASCII 001(ctrl-A)分隔,行之間以換行分隔。
如果數據不是以上述格式組織的,我們也可以指定分隔符,如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
目前,行分隔符不能指定,因爲它不是由Hive決定,而是由Hadoop分隔符。
對錶的指定列進行分桶,是一個好的方法,它可以有效地對數據集進行抽樣查詢。如果沒有分桶,則會進行隨機抽樣,由於在查詢的時候,需要掃描所有數據,因此,效率不高。以下例子描述了,在表page_view
的userid
列上進行分桶的例子:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
以上例子,通過一個userid
的哈希函數,表被分成32個桶。在每個桶中的數據,是以viewTime
升序進行存儲。這樣組織數據允許用戶有效地在這n個桶上進行抽樣。合適的排序使得內部操作充分利用熟悉的數據結構來進行更加有效的查詢。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
在這個例子,CLUSTERED BY
指定列進行分桶,以及創建多少個桶。行格式分隔符指定在hive表中,行如何存儲。在這種分隔符情況下,指定了字段是如何結束,集合項(數組和map)如何結束,以及map的key是如何結束的。STORED
AS SEQUENCEFILE
表示這個數據是以二進制格式進行存儲數據在hdfs上。對於以上例子的ROW FORMAT
的值和STORED
AS
表示系統默認值。
表名和列名不區分大小寫。
瀏覽表和分區
- 1
列出數據庫裏的所有的表,也可以這麼瀏覽:
- 1
這樣將會列出以page
開頭的表,模式遵循Java正則表達式語法。
- 1
列出表的分區。如果表沒有分區,則拋出錯誤。
- 1
列出表的列和列的類型。
- 1
列出表的列和表的其他屬性。這會打印很多信息,且輸出的風格不是很友好,通常用於調試。
- 1
列出列和分區的所有屬性。這也會打印出許多信息,通常也是用於調試。
修改表
對已有的表進行重命名。如果表的新名字存在,則報錯:
- 1
對已有表的列名進行重命名。要確保使用相同的列類型,且要包含對每個已存在列的一個入口(也就是說,就算不修改其他列的列名,也要把此列另上,否則,此列會丟失)。
- 1
對已有表增加列:
- 1
注意:
模式的改變(例如增加列),保留了表的老分區,以免它是一個分區表。所有對這些列或老分區的查詢都會隱式地返回一個null
值或這些列指定的默認值。
刪除表和分區
刪除表是相當,表的刪除會刪除已經建立在表上的任意索引。相關命令是:
- 1
要刪除分區。修改表刪除分區:
- 1
注意:此表或分區的任意數據都將被刪除,而且可能無法恢復。
加載數據
要加載數據到Hive表有許多種方式。用戶可以創建一個“外部表”來指向一個特定的HDFS路徑。用這種方法,用戶可以使用HDFSput
或copy
命令,複製一個文件到指定的位置,並且附上相應的行格式信息創建一個表指定這個位置。一旦完成,用戶就可以轉換數據和插入他們到任意其他的Hive表中。例如,如果文件/tmp/pv_2016-06-08.txt
包含逗號分隔的頁面訪問記錄。這需要以合適的分區加載到表page_view
,以下命令可以完成這個目標:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
其中,‘44’是逗號的ASCII碼,‘12’是換頁符(NP from feed,new page)。null
是作爲目標表中的數組和map類型插入,如果指定了合適的行格式,這些值也可以來自外部表。
如果在HDFS上有一些歷史數據,用戶想增加一些元數據,以便於可以使用Hive來查詢和操縱這些數據,這個方法是很有用的。
另外,系統也支持直接從本地文件系統上加載數據到Hive表。表的格式與輸入文件的格式需要相同。如果文件/tmp/pv_2016-06-08
包含了US
數據,然後我們不需要像前面例子那樣的任何篩選,這種情況的加載可以使用以下語法完成:
- 1
路徑參數可以是一個目錄(這種情況下,目錄下的所有文件將被加載),一個文件,或一個通配符(這種情況下,所有匹配的文件會上傳)。如果參數是目錄,它不能包含子目錄。同樣,通配符只匹配文件名。
在輸入文件/tmp/pv_2016-06-08.txt
非常大的情況下,用戶可以採用並行加載數據的方式(使用Hive的外部工具)。只要文件在HDFS上-以下語法可以用於加載數據到Hive表:
- 1
對於這個例子,我們假設數組和map在文件中的值爲null
。
更多關於加載數據到表的信息,請參考Hive Data Manipulation Language,創建外部表的另外一個例子請參考External Tables。
查詢和插入數據
- Simple Query
- Partition Based Query
- Joins
- Aggregations
- Multi Table/File Inserts
- Dynamic-Partition Insert
- Inserting into Local Files
- Sampling
- Union All
- Array Operations
- Map (Associative Arrays) Operations
- Custom Map/Reduce Scripts
- Co-Groups
Hive查詢操作在文檔Select,插入操作在文檔insert data into Hive Tables from queries和writing data into the filesystem from queries。
簡單的查詢
對於所有的活躍用戶,可以使用以下查詢格式:
- 1
- 2
- 3
- 4
注意:不像SQL,我們老是插入結果到表中。隨後我們會描述,用戶如何檢查這些結果,甚至把結果導出到一個本地文件。你也可以在Beeline或HiveCLI執行以下查詢:
- 1
- 2
- 3
這在內部將會重寫到一些臨時文件,並在Hive客戶端顯示。
基於查詢的分區
在一個查詢中,要使用什麼分區,是由系統根據where
在分區列上條件自動的決定。例如,爲了獲取所有2008年3月份,從域名xyz.com
過來的page_views,可以這麼寫查詢:
- 1
- 2
- 3
- 4
- 5
注意:在這裏使用的page_views.date
是用PARTITIONED
BY(date DATATIME, country STRING)
定義的.如果你的分區命名不一樣,那麼不要指望.date
能夠做你想做的事情,即無法獲得分區的優勢。
連接
表的連接可以使用以下命令:
- 1
- 2
- 3
- 4
想實現外連接,用戶可以使用LEFT OUTER
,RIGHT OUTER
或FULL
OUTER
關鍵詞來指示不同的外連接(左保留,右保留或兩端都保留)。例如,想對上面的查詢做一個FULL OUTER
,相應的語法可以像下面這樣:
- 1
- 2
- 3
- 4
爲了檢查key在另外一個表中是否存在,用戶可以使用LEFT SEMI JOIN
,正如以下例子一樣:
- 1
- 2
- 3
- 4
爲了連接多個表,可以使用以下語法:
- 1
- 2
- 3
- 4
注意:Hive只支持equi-joins。所以,把最大的表放在join
的最右邊,可以得到最好的性能。
聚合
統計用戶每個性別的人數,可以使用以下查詢:
- 1
- 2
- 3
- 4
可以同時做多個聚合,然而,兩個聚合函數不能同時用DISTINCT
作用於不同的列,以下情況是可以的(DISTINCT
作用於相同列):
- 1
- 2
- 3
- 4
然而,以下情況(DISTINCT
作用於不同的列)是不允許的:
- 1
- 2
- 3
- 4
多表/文件插入
聚合或簡單查詢的輸出可以插入到多個表中,或者甚至是HDFS文件(能夠使用HDFS工具進行操縱)。例如,如果沿用前面的“性別分類”,例子如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
第一個插入語句將結果插入到Hive表中,而第二個插入語句是將結果寫到HDFS文件。
動太分區插入
在前面的例子中,我們知道,在插入語句中,只能有一個分區。如果我們想加載到多個分區,我們必須像以下描述來使用多條插入語句:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
爲了加載數據到全部的country
分區到指定的日期。我們必須在輸入數據中爲每個country
增加一條插入語句。這是非常不方便的,因爲我們需要提前創建且知道已存在哪些country
分區列表。如果哪天這些country
列表變了,我們必須修改我們的插入語句,也應該創建相應的分區。這也是非常低效的,因爲每個插入語句可能都是轉換成一個MapReduce作業。
動態分區插入(Dynamic-partition insert)(或multi-partition插入)就是爲了解決以上問題而設計的,它通過動態地決定在掃描數據的時候,哪些分區應該創建和填充。這個新的特徵是在版本0.6.0加入的。在動態分區插入中,輸入列被評估,這行應該插入到哪個分區。如果分區沒有創建,它將自動創建這個分區。使用這個特徵,我們僅僅需要插入語桀犬吠堯來創建和填充所有需要的分區。另外,因爲只有一個插入語句,相應的也只有一個MapReduce作業。相比多個插入語句的情況,這將顯著地提高性能且降低Hadoop集羣負載。
以下是使用一個插入語句,加載數據到所有country
分區的例子:
- 1
- 2
- 3
與多條插入語句相比,動態分區插入有一些語法上的不同:
- country
出現在PARTITION
後面,但是沒有具體的值。這種情況,country
就是一個動態分區列。另一方面,dt
有一個值,這意味着它是一個靜態的分區列。如果一個列是動態分區列,它的值將會使用輸入列的值。目前,我們僅僅允許在分區條件的最後一列放置動態分區列,因爲分區列的順序,指示了它的層級次序(意味着dt
是根分區,country
是子分區)。我們不能這樣指定分區(dt,country=’US’),因爲這表示,我們需要更新所有的日期的分區且它的country
子分區是‘US’。
- 一個額外的
pvs.country
列被加入在查詢語句中。這對動態分區列來說,相當於輸入列。注意:對於靜態分區列,我們不需要添加一個輸入列,因爲在PARTITION語句中,它的值已經知道。注意:動態分區列的值(不是名字)查出來是有序的,且是放在select語句的最後。
動態分區插入的語義:
- 對於動態分區列,當已經此分區時,(例如,country='CA'
已存在dt
根分區下面)如果動態分區插入與輸入數據中相同的值(’CA’),它將會被重寫(overwritten)。
插入到本地文件
在某些場合,我們需要把輸出寫到一個本地文件,以便於能用excel表格打開。這可以使用以下命令:
- 1
- 2
- 3
抽樣
抽樣語句允許用戶對數據抽樣查詢,而不是全表查詢。當前,抽樣是對那些在CREATE TABLE
語句的CLUSTERED
BY
修飾的列上。以下例子,我們從表pv_gender_sum
表中的32個桶中,選擇第3個桶。
- 1
- 2
- 3
通常,TABLESAMPLE
的語法像這樣:
- 1
這個y
必須是桶的數量的因子或倍數,桶的數量是在創建表的時候指定的。抽樣所選的桶由桶大小,y和x共同決定。如果y和桶大小相等,則抽樣所選的桶是x對y的求模結果。
- 1
這將抽樣第3個和第19個桶。桶的編號從0開始。
tablesample
語句的另一方面:
- 1
這將抽取第3個桶的一半。
union all
這個語言也支持union all
,如果假設我們有兩個不同的表,分別用來記錄用戶發佈的視頻和用戶發佈的評論,以下例子是一個union all 的結果與用戶表再連接的查詢:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
數組操作
表的數組列可以這樣:
- 1
假設pv.friends 是類型ARRAY<INT>
(也就是一個整型數組),用戶可以通過索引號獲取數組中特定的元素,如下:
- 1
- 2
這個查詢得到的是pv.friends裏的第三個元素。
用戶也可以使用函數size
來獲取數組的長度,如下:
- 1
- 2
Map(關聯數組)操作
Map提供了類似於關聯數組的集合。這樣的結構不僅可以由程序創建。我們也將很快可以繼承這個。假設pv.properties是類型map<String,String>
,如下:
- 1
- 2
- 3
這將查詢表page_views
的‘page_type‘屬性。
與數組相似,也可以使用函數size
來獲取map的大小:
- 1
- 2
定製Map/Reduce腳本
通過使用Hive語言原生支持的特徵,用戶可以插入他們自己定製的mapper和reducer在數據流中。例如,要運行一個定製的mapper腳本script-map_script
和reducer腳本script-reduce_script
),用戶可以執行以下命令,使用TRANSFORM
來嵌入mapper和reducer腳本。
注意:在執行用戶腳本之前,表的列會轉換成字符串,且由TAB
分隔,用戶腳本的標準輸出將會被作爲以TAB
分隔的字符串列。用戶腳本可以輸出調試信息到標準錯誤輸出,這個信息也將顯示hadoop的詳細任務頁面上。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
map腳本樣本(weekday_mapper.py)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
當然,對於那些常見的select轉換,MAP和REDUCE都是“語法糖”。內部查詢也可以寫成這樣:
- 1
Co-Groups
在使用map/reduce的羣體中,cogroup
是相當常見的操作,它是將來自多個表的數據發送到一個定製的reducer,使得行由表的指定列的值進行分組。在Hive的查詢語言中,可以使用以下方式,通過使用union
all
和cluster by
來實現此功能。假設我們想對來自表actions_video
和action_comment
的行對uid
列進行分組,且需要發送他們到reducer_script
定製的reducer,可以使用以下語法:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
翻譯自: https://cwiki.apache.org/confluence/display/Hive/Tutorial