Hive是一個(gè)構(gòu)建在Hadoop上的數(shù)據(jù)倉(cāng)庫(kù)框架,是一個(gè)通用的、可伸縮的數(shù)據(jù)處理平臺(tái),它設(shè)計(jì)的目的是讓精通SQL技能的分析師能夠?qū)Υ娣旁贖DFS中的大規(guī)模數(shù)據(jù)集執(zhí)行查詢。
一、Hive的安裝
1.1 下載hive
地址:http://hive.apache.org/releases.html
下載需要的版本。
1.2 安裝hive
將下載的hive解壓到安裝目錄:
tar -zxvf 壓縮包 安裝目錄
1.3 設(shè)置環(huán)境變量
編輯文件:vim /etc/profile,添加如下內(nèi)容,請(qǐng)?zhí)顚懽约旱膶?shí)際路徑:
export HIVE_INNSTALL=/probd/apache-hive-1.2.1-bin
export PATH=$PATH:$HIVE_INNSTALL/bin
然后刷新環(huán)境變量:source /etc/profile
1.4 配置hive
進(jìn)入hive安裝目錄,修改conf/hive-site.xml文件,修改內(nèi)容如下(帶*號(hào)的需要請(qǐng)?zhí)顚懽约旱膶?shí)際值):
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?><configuration>
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:mysql://probd03:3306/metastore?createDatabaseIfNotExist=true</value>
<description>the URL of the MySQL database</description>
</property>
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>com.mysql.jdbc.Driver</value>
<description>Driver class name for a JDBC metastore</description>
</property>
<property>
<name>javax.jdo.option.ConnectionUserName</name>
<value>***</value>
</property>
<property>
<name>javax.jdo.option.ConnectionPassword</name>
<value>***</value>
</property>
<property>
<property>
<name>hive.metastore.warehouse.dir</name>
<value>/user/hive/warehouse</value>
</property>
<property>
<name>hive.aux.jars.path</name>
<value>file:////probd/probd-0.3.5/apache-hive-1.2.1-bin/lib/hive-hbase-handler-1.2.1.jar,file:////probd/probd-0.3.5/apache-hive-1.2.1-bin/lib/zookeeper-3.4.6.jar</value>
<description>The location of the plugin jars that contain implementations of user defined functions and serdes.</description>
</property>
<property>
<name>hive.exec.scratchdir</name>
<value>/user/hive/tmp</value>
</property>
<property>
<name>hive.querylog.location</name>
<value>/user/hive/log</value>
</property>
<property>
<name>hive.metastore.uris</name>
<value>***</value>
<description>格式:thrift://host:port</description>
</property>
<name>hive.server2.thrift.bind.host</name>
<value>***</value>
</property>
<property>
<name>hive.server2.thrift.port</name>
<value>10000</value>
</property>
</configuration>
修改conf/hive-env.sh,內(nèi)容如下,請(qǐng)?zhí)顚懽约旱膶?shí)際路徑:
HADOOP_HOME=/probd/hadoop-2.6.3
從hive-site.xml配置文件中看出,這里我們使用了mysql作為hive的metastore獨(dú)立數(shù)據(jù)庫(kù),所以,在運(yùn)行hive之前,請(qǐng)確保mysql數(shù)據(jù)庫(kù)已經(jīng)安裝并且已啟動(dòng)。其次還要確保metastore服務(wù)已經(jīng)啟動(dòng):

如果需要用到遠(yuǎn)程客戶端(比如 Tableau)連接到hive數(shù)據(jù)庫(kù),還需要啟動(dòng)hive service:

然后由于配置過(guò)環(huán)境變量,可以直接在命令行中輸入hive:

二、Hive的使用
2.1 創(chuàng)建表
通過(guò)上面的步驟,我們已經(jīng)啟動(dòng)了Hive的外殼環(huán)境(shell),Hive的外殼環(huán)境是我們與Hive交互、發(fā)出HiveQL命令的主要方式,HiveQL是Hive的查詢語(yǔ)言,相當(dāng)于SQL的一種“方言”,它的設(shè)計(jì)在很大程度上受MySQL的影響。因此,如果熟悉MySQL,你會(huì)覺得Hive很親切。
第一次啟動(dòng)時(shí),我們可以通過(guò)列出Hive的表來(lái)檢查Hive是否正常工作,此時(shí)應(yīng)該沒有任何表。
hive> show tables;
ok
Time taken: 10.429 seconds
我們看到第一次所花費(fèi)的時(shí)間還是挺長(zhǎng)的,這是因?yàn)橄到y(tǒng)采用lazy策略,所以直到此時(shí)才在機(jī)器上創(chuàng)建metastore數(shù)據(jù)庫(kù)(該數(shù)據(jù)庫(kù)把相關(guān)文件放在運(yùn)行hive命令那個(gè)位置的metastore_db的目錄中)。
我們有一個(gè)描述學(xué)生的文件student.txt,文件里面的內(nèi)容如下:
zhangsan,14,165
cuihua,13,160
wangwu,15,168
現(xiàn)在我們要把這些數(shù)據(jù)存放在hive中。和RDBMS一樣,Hive把數(shù)據(jù)組織成表。我們使用CREATE TABLE語(yǔ)言為學(xué)生的數(shù)據(jù)新建一個(gè)表:
CREATE TABLE students(name STRING,age INT,stature INT)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t';
第一行聲明一個(gè)students表,包含三列name,age,stature。還必須指明每一列的數(shù)據(jù)類型,這里我們指定了姓名為字符串類型,年齡和身高都是整型。
第二行的ROW FORMAT DELIMITED是HiveQL所特有的,這個(gè)句子聲明的是數(shù)據(jù)文件的每一行是由制表符分隔的文本。Hive按照這一格式讀取數(shù)據(jù):每行3個(gè)字段,分別對(duì)應(yīng)表中的3列,以換行符分隔。
第三行FIELDS TERMINATED BY '\t'表示字段間以制表符分隔。
2.2 導(dǎo)入數(shù)據(jù)
現(xiàn)在,已經(jīng)創(chuàng)建了表,接下來(lái),我們可以向Hive 導(dǎo)入數(shù)據(jù),導(dǎo)入數(shù)據(jù)有三種方式:
1)使用LOAD DATA操作,把文件復(fù)制或移動(dòng)到表的目錄中,從而把數(shù)據(jù)導(dǎo)入Hive表。
2)使用INSERT語(yǔ)句把數(shù)據(jù)從一個(gè)Hive表填充到另一個(gè)。
3)在新建表的時(shí)候使用CTAS結(jié)構(gòu),即CREATE TABLE ... AS SELECT。我們先來(lái)看這第一種方式:
LOAD DATA LOCAL INPATH '/student.txt'
OVERWRITE INTO TABLE students;
這一行命令告訴Hive把指定的本地文件放入其倉(cāng)庫(kù)目錄中。這只是一個(gè)簡(jiǎn)單的文件系統(tǒng)操作。這個(gè)操作并不解析文件或把它存儲(chǔ)為內(nèi)部數(shù)據(jù)庫(kù)格式,因?yàn)镠ive并不強(qiáng)制使用任何特定的文件格式。文件以原樣逐字存儲(chǔ),Hive并不會(huì)對(duì)這個(gè)文件進(jìn)行修改。
在本例中,我們把Hive表存儲(chǔ)在本地文件系統(tǒng)中(fs.default.name設(shè)為默認(rèn)值 file:///)。在Hive的倉(cāng)庫(kù)目錄中,表存儲(chǔ)為目錄。倉(cāng)庫(kù)目錄由選項(xiàng)hive.metastore.warehouse.dir控制,默認(rèn)值為/user/hive/warehouse。
這樣,student表的文件便可以在本地文件系統(tǒng)的/user/hive/warehourse/students目錄中找到:
% ls /user/hive/warehourse/students/
student.txt
LOAD DATA 語(yǔ)句中的OVERWRITE關(guān)鍵字告訴Hive刪除表對(duì)應(yīng)目錄中已有的所有文件,如果省去這個(gè)關(guān)鍵字,Hive就簡(jiǎn)單的把新的文件加入目錄,并只替換掉同名文件。
接下來(lái)我們來(lái)看INSERT語(yǔ)句導(dǎo)入數(shù)據(jù):
INSERT OVERWRITE TABLE target
SELECT col1,col2
FROM source;
OVERWRITE關(guān)鍵字在這種情況下是強(qiáng)制的。這意味著目標(biāo)表中的內(nèi)容會(huì)被SELECT語(yǔ)句的結(jié)果替換掉。
在HiveQL中,可以把ISERT語(yǔ)句倒過(guò)來(lái),把FROM子句放在最前面,結(jié)果是一樣的:
FROM source
ISERT OVERWRITE TABLE target
SELECT col1,col2;
也可以在同一個(gè)查詢語(yǔ)句中使用多個(gè)INSERT子句。此時(shí),這樣的語(yǔ)法會(huì)讓查詢的含義更加清楚。這種“多表插入”方法比使用多個(gè)單獨(dú)的INSERT語(yǔ)句效率要高,因?yàn)橹恍枰獟呙枰槐樵幢砭涂梢陨啥鄠€(gè)不想交的輸出:
FROM source
INSERT OVERWRITE TABLE target1
SELECT col1,col2
INSERT OVERWRITE TABLE target2
SELECT col1,COUNT(1)
GROUP BY col1
INSERT OVERWRITE TABLE target3
SELECT col1,COUNT(1)
WHERE ...
這里只有一個(gè)源表,但有三個(gè)表用于存放針對(duì)這個(gè)源表的三個(gè)不同查詢鎖產(chǎn)生的結(jié)果。
再來(lái)看第三種方式:CREATE TABLE ... AS SELECT語(yǔ)句。
把hive查詢的輸出結(jié)果存放到一個(gè)新的表往往非常方便,但是是因?yàn)檩敵鼋Y(jié)果太多,不適宜顯示在控制臺(tái)或基于輸出結(jié)果還有其它后續(xù)處理。
新表的列的定義是從SELECT子句所檢索的列導(dǎo)出的。在下面的查詢中,target表有兩列,分別名為col1和col2,它們的數(shù)據(jù)類型和源表中對(duì)應(yīng)的列相同:
CREATE TABLE target
AS
SELECT col1,col2
FROM source;
CTAS操作是源自原子的,因此如果SELECT查詢由于某種原因失敗,新表就不會(huì)被創(chuàng)建。
2.3 查詢數(shù)據(jù)
通過(guò)前面例子的操作,數(shù)據(jù)現(xiàn)在已經(jīng)在Hive中,我們可以對(duì)它運(yùn)行一個(gè)查詢:
hive> SELECT name,MAX(age)
> FROM students
> WHERE age!=15
> AND (stature=160 OR stature=165)
> GROUP BY name;
zhangsan,14,165
這個(gè)SQL查詢沒有什么特別的,它的優(yōu)勢(shì)在于把這個(gè)查詢語(yǔ)句轉(zhuǎn)化為一個(gè)MapReduce作業(yè)并為我們執(zhí)行這個(gè)作業(yè),然后把結(jié)果打印輸出到控制臺(tái)。
2.4 讀時(shí)模式和寫時(shí)模式
在傳統(tǒng)的數(shù)據(jù)庫(kù)里,表的模式是在數(shù)據(jù)加載時(shí)強(qiáng)制確定的。如果在加載時(shí)發(fā)現(xiàn)數(shù)據(jù)不符合模式,則拒絕加載數(shù)據(jù)。因?yàn)閿?shù)據(jù)在寫入數(shù)據(jù)庫(kù)時(shí)對(duì)照模式進(jìn)行檢查,因此這一設(shè)計(jì)有時(shí)被稱為“寫時(shí)模式”。
而在Hive中,對(duì)數(shù)據(jù)的驗(yàn)證并不在加載數(shù)據(jù)的時(shí)候進(jìn)行,而是在查詢時(shí)進(jìn)行,這被稱為“讀時(shí)模式”。
用戶需要在這兩種方法之間進(jìn)行權(quán)衡。讀時(shí)模式可以使數(shù)據(jù)加載非常迅速,這是因?yàn)樗恍枰x取數(shù)據(jù),進(jìn)行“解析”,再進(jìn)行序列化以數(shù)據(jù)庫(kù)內(nèi)部格式存入磁盤。數(shù)據(jù)加載操作僅僅是問(wèn)文件復(fù)制或移動(dòng)。這一方法更為靈活:試想,針對(duì)不同的分析任務(wù),同一個(gè)數(shù)據(jù)可能會(huì)有兩個(gè)模式。Hive使用“外部表”時(shí),這種情況也是可能發(fā)生的。
寫時(shí)模式有利于提升查詢性能。因?yàn)閿?shù)據(jù)庫(kù)可以對(duì)列進(jìn)行索引,并對(duì)數(shù)據(jù)進(jìn)行壓縮。但作為權(quán)衡,此時(shí)加載數(shù)據(jù)會(huì)花更多時(shí)間。此外,在很多加載時(shí)模式未知的情況下,因?yàn)椴樵兩形创_定,因此不能決定使用何種索引。
三、Metastore
metastore是Hive元數(shù)據(jù)的集中存放地。metastore包括兩部分:服務(wù)和后臺(tái)數(shù)據(jù)的存儲(chǔ)。
3.1 內(nèi)嵌metastore:默認(rèn)情況下,metastore服務(wù)和Hive服務(wù)運(yùn)行在同一個(gè)JVM中,它包含一個(gè)內(nèi)嵌的以本地磁盤作為存儲(chǔ)的Derby數(shù)據(jù)庫(kù)實(shí)例,這種稱為“內(nèi)嵌metastore配置”。使用內(nèi)嵌的metastore是最簡(jiǎn)單的方法,但是每次只有一個(gè)內(nèi)嵌的Derby數(shù)據(jù)庫(kù)可以訪問(wèn)某個(gè)磁盤上的數(shù)據(jù)庫(kù)文件,這就意味著一次只能為每個(gè)metastore打開一個(gè)Hive會(huì)話。所以一般實(shí)際環(huán)境中是不使用這種配置的。
3.2 本地metastore: 如果要支持多會(huì)話以及多用戶,需要使用一個(gè)獨(dú)立的數(shù)據(jù)庫(kù)。這種配置成為“本地metastore”,因?yàn)閙etastore服務(wù)仍然和Hive服務(wù)運(yùn)行在同一個(gè)進(jìn)程中,但連接的卻是在另一個(gè)進(jìn)行中運(yùn)行的數(shù)據(jù)庫(kù),在同一臺(tái)機(jī)器上或在遠(yuǎn)程機(jī)器上。任何JDBC兼容的數(shù)據(jù)庫(kù)都可以通過(guò)下表列出的javax.jdo.option.*這個(gè)屬性配置來(lái)供metastore使用。

我們常用MySQL來(lái)作為獨(dú)立的metastore,此時(shí)javax.jdo.option.ConnectionURL應(yīng)該設(shè)為jdbc:mysql://host/dbname?createDatabaseIfNotExist=true,而javax.jdo.option.ConnectionDriverName則設(shè)置為com.mysql.jdbc.Driver(當(dāng)然還需要設(shè)置用戶名和密碼)。MySQL的JDBC驅(qū)動(dòng)jar包必須在Hive的類路徑中,把這個(gè)包放到Hive的lib目錄下即可。
3.3 遠(yuǎn)程metastore: 更進(jìn)一步,還有一種metastore配置稱為“遠(yuǎn)程metastore”,這種配置下,一個(gè)或多個(gè)metastore服務(wù)器和Hive服務(wù)運(yùn)行在不同的進(jìn)程中,這樣一來(lái),數(shù)據(jù)庫(kù)層可以完全置于防火墻后,客戶端則不需要數(shù)據(jù)庫(kù)憑據(jù)(用戶名和密碼),從而提供了更好的可管理性和安全。可以通過(guò)把hive.metastore.local設(shè)為false,hive.metastore.uris設(shè)為metastore服務(wù)器URI,其值的形式為thrift://host:port(如果有多個(gè)服務(wù)器,各個(gè)URI之間用逗號(hào)分隔),即可把Hive服務(wù)設(shè)為使用遠(yuǎn)程metastore。
四、表
Hive的表在邏輯上由存儲(chǔ)的數(shù)據(jù)和描述表中數(shù)據(jù)形式的相關(guān)元數(shù)據(jù)組成。數(shù)據(jù)一般存放在HDFS中,或是其他任何Hadoop文件系統(tǒng)中,包括本地文件系統(tǒng)或S3。Hive把元數(shù)據(jù)存儲(chǔ)在關(guān)系型數(shù)據(jù)庫(kù)中(metastore)。
4.1 托管表和外部表
在Hive中創(chuàng)建表時(shí),默認(rèn)情況下Hive負(fù)責(zé)管理數(shù)據(jù),這意味著Hive把數(shù)據(jù)移到它的倉(cāng)庫(kù)目錄中,我們稱為“托管表”。另一種選擇是創(chuàng)建一個(gè)“外部表”,這會(huì)讓Hive到倉(cāng)庫(kù)目錄以外的位置訪問(wèn)數(shù)據(jù)。
這兩種表的區(qū)別表現(xiàn)在LOAD和DROP命令的語(yǔ)義上。先來(lái)看托管表。
加載數(shù)據(jù)到托管表時(shí),Hive把數(shù)據(jù)移到倉(cāng)庫(kù)目錄。列如我們上面的那個(gè)例子:
LOAD DATA LOCAL INPATH '/student.txt' INTO TABLE students;
這里因?yàn)榧恿薒OCAL,所以是把文件復(fù)制到hive的students表的倉(cāng)庫(kù)目錄中,如果不加,則為移動(dòng),即file:///user/hive/warehourse/students,當(dāng)然也可以移到HDFS文件系統(tǒng)上。
如果要丟棄一個(gè)表,可以使用一下語(yǔ)句:
DROP TABLE students;
這個(gè)表,包括它的元數(shù)據(jù)和數(shù)據(jù),都會(huì)被一起刪除。在這里要重復(fù)強(qiáng)調(diào),因?yàn)樽畛醯腖OAD是一個(gè)移動(dòng)操作,而DROP是一個(gè)刪除操作,所以數(shù)據(jù)會(huì)徹底消失。這就四Hive所謂的“托管數(shù)據(jù)”的含義。
對(duì)于外部表而言,這兩個(gè)操作的結(jié)果就不一樣了:由你自己來(lái)控制數(shù)據(jù)的創(chuàng)建和刪除。外部數(shù)據(jù)的位置需要在創(chuàng)建表的時(shí)候指明:
CREATE EXTERNAL TABLE external_table(dummy STRING)
LOCATION '/user/tom/external_table';
LOAD DATA INPATH '/data.txt' INTO TABLE external_table;
使用EXTERNAL關(guān)鍵字后,Hive知道數(shù)據(jù)并不由自己管理,因此不會(huì)把數(shù)據(jù)移到自己的倉(cāng)庫(kù)目錄。事實(shí)上,在定義時(shí),它甚至不會(huì)檢查這一外部位置是否存在。這是一個(gè)非常有用的特性,因?yàn)檫@意味著你可以把創(chuàng)建數(shù)據(jù)推遲到創(chuàng)建表之后才進(jìn)行。
丟棄外部表時(shí),Hive不會(huì)碰數(shù)據(jù),而是只會(huì)刪除元數(shù)據(jù)。
那么,應(yīng)該如何選擇使用哪種表呢?在多數(shù)情況下,這兩種方式?jīng)]有太大的區(qū)別(當(dāng)然DROP除外),因此這是個(gè)人喜好的問(wèn)題。作為一個(gè)經(jīng)驗(yàn)法則,如果所有處理都是由Hive來(lái)完成,應(yīng)該使用托管表。但如果要用Hive和其他工具來(lái)處理同一個(gè)數(shù)據(jù)集,應(yīng)該使用外部表。普遍的用法是把存放在HDFS的初始數(shù)據(jù)集用作外部表進(jìn)行使用,然后用Hive的變換功能把數(shù)據(jù)移到托管的Hive表中,這一方法反之也成立——外部表可以用于從Hive導(dǎo)出數(shù)據(jù)供其他應(yīng)用程序使用。
4.2 表的創(chuàng)建
前面我們已經(jīng)講了如何創(chuàng)建一個(gè)表,即:
CREATE TABLE students(name STRING,age INT,stature INT)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t';
里面語(yǔ)句的意思這里不再贅述。
4.3 表的修改
由于Hive使用“讀時(shí)模式”(導(dǎo)入數(shù)據(jù)時(shí)對(duì)數(shù)據(jù)的驗(yàn)證并不在導(dǎo)入的時(shí)候進(jìn)行,而是在查詢時(shí)進(jìn)行),所以創(chuàng)建表以后,它非常靈活低支持對(duì)表定義的修改。但一般需要警惕,在很多情況下,要由我們自己來(lái)確保修改數(shù)據(jù)以符合新的結(jié)構(gòu)。
可以使用 ALTER TABLE語(yǔ)句來(lái)重命名表:
ALTER TABLE source RENAME TO target;
在更新表的元數(shù)據(jù)以外,ALTER TABLE語(yǔ)句還把表目錄移到新名稱所對(duì)應(yīng)的目錄下。上面的那句結(jié)果是 /user/hive/warehouse/source 被重命名為/user/hive/warehouse/target。對(duì)于外部表,這個(gè)操作只更新元數(shù)據(jù),而不會(huì)移動(dòng)目錄。
Hive允許修改列的定義,添加新的列,甚至用一組新的列替換表內(nèi)已有的列。
如添加一個(gè)新列:
ALTER TABLE target ADD COLUMNS (col3 STRING)
新的col3被添加在已有列的后面。數(shù)據(jù)文件并沒有更新,因此原來(lái)的查詢會(huì)為col3的所有值返回空值null(當(dāng)然,除非文件中原來(lái)就已經(jīng)有額外的字段)。因?yàn)镠ive不允許更新已有的記錄,所以需要使用其它機(jī)制來(lái)更新底層的文件。為此,更常用的做法是創(chuàng)建一個(gè)定義了新列的新表,然后使用SELECT語(yǔ)句把數(shù)據(jù)填充進(jìn)去。
4.4 表的刪除
DROP TABLE語(yǔ)句用于刪除表的數(shù)據(jù)和元數(shù)據(jù)。如果是外部表,就只刪除元數(shù)據(jù)——數(shù)據(jù)不會(huì)受到影響。
如果要?jiǎng)h除表內(nèi)的所有數(shù)據(jù),但要保留表的定義,刪除數(shù)據(jù)文件即可,例如:
hive>
dfs -rmr /user/hive/warehouse/my_table;
Hive把缺少文件(或根本沒有表對(duì)應(yīng)的目錄)的表認(rèn)為是空表。
另外一種達(dá)到類似目的的方法是使用LIKE關(guān)鍵字創(chuàng)建一個(gè)與第一個(gè)表模式相同的新表。如:
CREATE TABLE new_table LIKE existing_table;