Hive配置運行及表的操作
配置Hive
Hive的配置文件名為hive-site.xml,你可以在Hive安裝目錄下的conf目錄下找到這個文件。如果你發現該目錄下沒有這個文件,你可以通過復制hive-default.xml.template來生成該文件。
當然,你也可以在進入hive時指定參數來明確指明配置文件所在目錄。比如:
hive --config /home/user/hive-conf
你也可以在進入hive時,通過命令行指定特定的配置參數值,比如:
hive --config fs.defalut.name=localhost:9000
以上命令在進入hive時,設置文件系統為localhost。如果在配置文件中同樣包含對該屬性值的設置,則命令行的設置將覆蓋配置文件中的設置。
你也可以在進入hive后,通過set命令進行設置,比如:
set fs.default.name=localhost:9000;
如以上所述,有很多方式都可以設置配置屬性值。如果在多個地方對同一屬性值進行了設置,則其優先級由高到低依次為:
l Hive set命令
l 命令行—config設置
l Hive-site.xml
l Hive-default.xml
l Hadoop-site.xml(或core-site.xml等)
l Hadoop-default.xml
注意:以上提到的hadoop-site.xml及haddop-default.xml文件為Hadoop的配置文件,默認在Hadoop安裝目錄下的etc/hadoop目錄下。Pig通過Haddop相關的環境變量來獲取這些配置文件中的關聯信息。
日志
Hive默認的日志文件為/tmp/{user}/hive.log。當你運行hive過程中碰到問題需要分析時,查看這個日志文件是一個非常好的辦法。Hive默認使用log4j來記錄日志。
Hive服務
前面提到的Shell只是Hive提供的服務之一。你可以在命令行通過--service選項指定運行某一服務。Hive提供的服務包括:
1) Cli
命令行接口(command lineinterface),即Shell。通過以下命令進入shell:
hive --service cli
注意:cli為默認方式,即以上命令等同于不帶任何參數的hive命令。
2) Hiveserver
通過提供Thrift接口服務來運行Hive服務,可供多種客戶端通過Thrift來于Hive進行通訊。Thrift服務我們后續來講。
3) Hwi
Hive的Web接口。
要使用hwi,需要先安裝ant,并配置ant的環境變量,比如:
export ANT_LIB=/user/share/ant/lib
完成后,運行以下命令:
Hive –service hwi
然后通過localhost:9999/hwi訪問。
注意:
訪問hwi時,可能會碰到各種各樣的問題,比如Unable to find a javac compiler等等。此時,請嘗試以下操作:
l 找到jdk目錄下的tools.jar,復制并拷貝到hive的lib目錄下。
l 找到ant目錄下的ant.jar和ant-launcher.jar文件,復制并拷貝到hive的lib目錄下。
4) jar
與Hadoop Jar的使用方法類似。
5) metadata
默認情況下,metadata和hive運行在同一進程中。通過此方式,可以讓metadata以單獨的進程運行。
6) Hive客戶端
有很多種方式可以通過客戶端連接到Hive服務器。
l Thrift 客戶端
l JDBC驅動
l ODBC驅動
我們在以后的實例中,逐個講解。
Metastore
Metastore包含兩個部分:服務和數據存儲。默認情況下,metastore服務和hive服務運行在同一個jvm中,包含一個內嵌的本地Derby數據庫實例。
使用內嵌的數據庫存儲metastore元數據是最簡單的處理方式。然而這就意味著同一時間只有一個Derby數據庫實例可以訪問數據庫磁盤文件,此時如果你啟動第二個回話,Hive就會報錯。
如果希望Hive支持多會話,我們就需要一個獨立的數據庫支持。這種方式我們稱之為本地Metastore(Local metastore)。在這種工作方式下,Metastore服務仍然和Hiveservice工作在同一個進程中,但是連接到一個獨立的數據庫服務進程(甚至可以是一個遠程的數據庫服務進程)。一般情況下支持JDBC的數據庫都可以配置作為該數據庫服務支持程序。
MySQL是一個非常合適的選擇,用來支持Metastore數據存儲。此種情況下,我們需要設置:
javax.jdo.option.ConnectionURL = jdbc:mysql://host/dbname?createDatabaseIfNotExist=true
javax.jdo.option.ConnectionDriverName = com.mysql.jdbc.Driver
javax.jdo.option.ConnectionUserName = username
javax.jdo.option.ConnectionPassword = password
注意:請確保對應的JDBC驅動的jar文件存在于hive的classpath,比如hive的lib目錄下。
另外一種方式我們稱之為遠程metastore。在這種配置下,Metastore服務運行在自己獨立的進程中,其他進程通過Thrift和metastore服務進行通訊。這種方式的好處之一就是客戶端不需要提供JDBC登錄信息等敏感數據。
注意:
Local或者Remote方式不是以數據庫是否在本地來區分的,也就是說remote方式下database數據庫仍然可以和Hive運行在同一個機器上。
請參照Hive官方文檔了解更多關于以上三種方式的詳細信息。
和傳統數據庫的比較
Hive某些方面和傳統數據庫是基本一致的,比如SQL語法。然而Hive的基于Mapreduce和hdfs的特性,決定了它和傳統數據庫是有很多顯著區別的。
讀時模式和寫時模式
傳統數據庫的表模式是有嚴格定義的,而且在數據加載時會驗證數據是否符合模式的要求,如果不符合則會拒絕載入數據。比如將一個字母插入到數值字段時,數據庫服務器會報錯從而導致操作失敗。我們稱此模式為“寫時模式”。
Hive則采用了不同的模式。它對數據的驗證不是發生在數據載入階段,而是發生在數據讀取階段。這就意味著,載入一個不符合模式的數據文件不會發生錯誤,但是一旦你執行譬如select語句時,則會發生錯誤。我們稱這種方式為“讀時模式”。
以上兩種方式的優點和缺點都很明顯。比如讀時模式在數據載入時非常快。而寫時模式則可以對字段進行索引,從而顯著提高查詢效率。
更新,事務和索引
更新,事務和索引是傳統數據的重要特性,但是Hive目前還不支持這幾種特性。
后續的Hive版本中會充分考慮這些特性。Hbase和Hive的集成正是了解這些特性的很好的例子。我們在Hbase中再做詳細介紹。
HiveQL
Hive所使用的SQL語言我們稱之為HiveQL,但是它并不完全支持SQL-92規范,畢竟Hive和傳統的數據庫系統是有不同應用場景的。再者,如果你發現SQL-92支持的某項功能HiveQL不支持,你也可以基于現有的HiveQL采用一些變通的方式來達成。
數據類型
Hive提供了基本數據類型和一些復雜數據類型。
基本數據類型包括:TINYINT,SMALLINT, INT, BIGINT, FLOAT, DOUBLE, BOOLEAN, STRING, BINARY, TIMESTAP。
復雜數據類型包括:ARRAY,MAP,STRUCT。
以上數據類型大部分通過字面意思即可知曉,我們不做詳述,稍后的實例中我們展開其用法。
數據類型轉換
和其它編程語言類似,Hive會在需要時執行一些自動轉換操作。比如一個表達式期望一個int,而實際提供的值是tinyint,則hive會自動將tinyint轉換為int。但是反過來,將int轉化為tinyint這種隱式轉換hive是不會發生的。除非你進行顯式轉換,比如CAST('1' AS INT)。
復雜數據類型
Hive提供了三種復雜數據類型:array,map, struct。
比如以下語句:
CREATE TABLE complex (
col1 ARRAY<INT>,
col2 MAP<STRING, INT>,
col3 STRUCT<a:STRING, b:INT, c:DOUBLE>
);
//TODO
操作與函數
Hive提供了常用的SQL操作,比如等值判斷x=’a’,空值判斷x isnull,模式匹配 x like ‘a%’等。
Hive提供了豐富的內置函數。你可以通過showfunctions來獲取函數列表。
當然,你也可以自己編寫自定義函數。稍后講解。
表
和傳統數據庫表的概念類似,hive表由表數據和描述表結構等的元數據組成。表數據一般存儲在HDFS中,當然也可以存在在其他文件系統,比如S3中;表的元數據存儲在一個關系型數據庫系統中,比如MySQL,而不是存儲在hdfs中。
注意:
傳統的關系數據庫支持多數據庫,比如它們都提供了create database語句。Hive早期版本不提供此功能,即所有表都存放在default數據庫中。而最近幾個版本的hive已經支持多數據庫,即你也可以在hive中使用create database語句創建數據庫,使用use語句卻換當前數據庫。
托管表和外部表(Managed table,External table)
默認情況下,當你創建Hive表時,hive將復雜管理表數據,即Hive會把表數據存儲到它的數據倉庫目錄下(warehouse directory)。這種方式創建的表我們稱之為托管表(managed table)。
另一種方式是創建一個外部表(External table)。此時,我們只需要告訴hive數據的外部引用地址,Hive本身不會在自己的數據倉庫目錄下存儲這些數據。
以下命令創建一個托管表并加載數據:
create table student(classNostring, stuNo string, score int) row format delimited fields terminated by ',';
load data local inpath '/home/user/input/student.txt'overwrite into table student;
執行以上命令時,hive將本地文件系統中的student.txt數據文件復制到hive的數據倉庫目錄下。此例中,數據文件地址為hdfs://localhost:9000/user/hive/warehouse/student/student.txt
此時,如果你執行以下語句:
DROP TABLE student;
Student表將會被刪除,包括表數據,元數據(metadata)。
注意:
關于元數據的存儲,默認情況下Hive使用內嵌的Derby數據庫來存儲。
在配置文件中你可以看到:
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:derby:;databaseName=metastore_db;create=true</value>
<description>JDBC connect string for aJDBC metastore</description>
</property>
注意紅色部分。元數據是存放在當前目錄下的,這就意味著以下場景會發生:
第一步:當前工作目錄為/root/A。進入hive shell后,hive會在/root/A目錄下創建元數據。
第二步:創建表student。
第三步:退出hive。
第四步:更改當前工作目錄為/root/B,再次進入hiveshell。
第五步:使用命令show tables。你會發現先前創建的student表并不存在。原因就是配置文件中,hive的元數據是存儲在當前目錄的metastore_db數據庫中的。這一步hive又會在/root/B目錄下創建一個新的metastore_db數據庫。
如果你希望將metastore存儲在固定位置,則你需要更改配置如下,此時無論你當前工作目錄是什么,hive都會讀取這個固定目錄下的metastore數據庫:
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:derby:;databaseName=/opt/hive-0.11.0/metastore_db;create=true</value>
<description>JDBC connect string for aJDBC metastore</description>
</property>
以下命令創建一個外部表:
Create external tableteacher(classNo string, teacherName string) row format delimited fields terminatedby ',' location '/user/hive/external/teacher';
注意:
location后面跟的是目錄名,而不是文件名。Hive會將整個目錄下的文件加載。目錄為hdfs下的目錄而不是本地文件系統的目錄。(因為此例中hive是工作在hdfs文件系統上的)
執行以下語句:
LOAD DATA INPATH '/user/hive/external/teacher_02.txt' INTO TABLE teacher;
注意:
此語句將會把/user/hive/external/teacher_02.txt'文件移動到/user/hive /external/teacher/目錄下;如果源數據在本地文件系統,則需要在INPATH前加上LOCAL選項,此時Hive會將源數據復制到表目錄下。
執行以下語句刪除外部表:
Drop table teacher;
以上語句只會刪除表的元數據(metastore),不會刪除表數據文件本身(本例中為teacher.txt和teacher_02.txt)。
分區和桶(Partitions,Buckets)
Hive通過分區(partitions)來組織表。這是一種通過分區列(partition column,比如日期)來對表進行劃分的粗粒度管理方式。
表或分區可以進一步劃分為桶(buckets)。桶為表數據提供了額外的結構信息,以提高查詢的效率。比如按用戶Id進行桶的劃分,能顯著提高基于用戶ID的查詢效率。
分區
假設我們有大量的日志文件,每一個日志記錄包含一個時間戳。我們以日期來對它進行分區,這樣的話,同一天的日志記錄就會被存放在同一個分區里。這就使得我們在查詢某一天(或多天)的日志記錄時能夠獲得非常高的查詢效率,因為hive只需要掃描特定的分區文件。
表的分區可以基于多個維度。比如,在按日期進行日志記錄分區的同時,再進一步根據日志類型進行子分區(subpartition)。這樣,當我們需要查詢特定類型的日志記錄時,就能夠獲得更高效率的查詢。
運行以下命令
Create external table yarnlog (createdtime string,category string,message string) partitioned by (date string,type string) rowformat delimited fields terminated by '\t';
注意:
Partitioned by子句緊跟在createtable后面。
Hive為yarnlog表創建了5個字段,分別為createdtime,category,message,date,type。可以用describeyarnlog來查看。
運行以下語句加載數據:
load data local inpath'/home/user/input/log/20140111/info' into table yarnlog partition(date='2014-01-11',type='INFO');
load data local inpath '/home/user/input/log/20140111/warn'into table yarnlog partition(date='2014-01-11',type='warn');
load data local inpath'/home/user/input/log/20140112/warn' into table yarnlogpartition(date='2014-01-12',type='warn');
load data local inpath'/home/user/input/log/20140112/info' into table yarnlogpartition(date='2014-01-12',type='INFO');
注意:
以上語句中的紅色部分。加載數據時,必須明確指明partition的各個字段值。Hive不能從數據文件中獲得任何partition字段值,因為load操作只復制數據文件而不會讀取文件內容。
加載完成后,hdfs中的/user/hive/warehouse目錄下的子目錄結構如下:
Yarnlog/
---------date=2014-01-11/
-------------------------------type=INFO/
-------------------------------type=warn/
---------date=2014-01-12/
-------------------------------type=INFO/
-------------------------------type=warn/
執行語句show partitions yarnlog,輸出如下:
date=2014-01-11/type=INFO
date=2014-01-11/type=warn
date=2014-01-12/type=INFO
date=2014-01-12/type=warn
分區列(partition column)是表的正式列,你可以像使用其他列一樣使用它。比如運行以下語句:
select message from yarnlog where type='warn';
此語句執行時僅僅掃描特定的分區文件。
備注:
以上使用到的日志文件格式為(tab分隔,你可以自己造一些測試數據):
2014-01-1208:51:04,818 WARN Startingcontainer_1389456842640_0006_01_000002
桶
使用桶的原因有兩個:提高特定條件下的查詢效率;更高效的執行取樣操作。
假如有兩個表按照字段uiserId進行join操作。Hive需要根據左表的某一個特定的userId來查找右表中關聯的記錄。如果這兩個表都按照相同的方式進行了桶劃分,則此時Hive在做匹配操作時,針對左表每一個特定的userId,只需要掃描右表的一個桶數據就可以了。(這么講解可能不太恰當,因為實際操作是由Hadoop的map操作來完成的)
執行以下語句:
create table student_bucketed (classNostring, stuNo string, score int) clustered by (classNo) sorted by (classNo)into 4 buckets row format delimited fields terminated by ',';
set hive.enforce.bucketing =true;
insert overwrite tablestudent_bucketed select * from student;
以上語句中的sorted by子句是可選的,該子句申明桶為排序桶,可以進一步提高map端的連接效率。桶的劃分是根據列值的hash值對桶個數的求余而來。
set hive.enforce.bucketing =true語句的作用是告訴Hive,Reduce的個數是和桶的個數一致的。如果不設置此屬性值,則需要用戶指定mapred.reduce.tasks屬性值。
執行以下語句對表數據取樣,返回大概三分之一的桶數據:
hive> select * fromstudent_bucketed tablesample(bucket 1 out of 3 on classno);
存儲格式(storage fromate)
Hive存儲格式涉及到兩個方面:行格式(rowformat)和文件格式(file format)。
創建表的時候如果沒有明確指明格式,則hive采用的默認格式為:文本文件,每行一個數據行。
Hive默認的行內分隔符不是tab,而是Ctrl+A(對應的ascii碼為1)。
Hive默認的集合內分隔符是Ctrl+B,用于分隔array,map以及struct中的鍵值。
表中各行以換行符分隔。
Create table時不指定任何分隔符,則其等價于以下語句:
CREATE TABLE 表名 (表字段)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\001'
COLLECTION ITEMS TERMINATED BY '\002'
MAP KEYS TERMINATED BY '\003'
LINES TERMINATED BY '\
STORED AS TEXTFILE;
以上STORE AS TEXTFILE指明文件格式為文本文件。
二進制存儲格式請參照官方文檔。
導入數據
除了前面提到的load語句外,我們還可以從已有的其他hive表中加載數據到特定的表。如前面提到的語句:
insert overwrite tablestudent_bucketed select * from student;
這個語句將會查詢student表中的所有數據并插入到student_bucketed表中。當然你還可以添加partition選項,這就是所謂的動態分區插入功能(dynamic-partition insert),不過你需要通過set hive.exec.dynamic.partition= true來啟用該功能。
注意:
以上語句中的overwrite是必須的,這就意味著目標表(或分區)將會被覆蓋。Hive目前還不支持往已有表(分區)中添加新紀錄的功能。
Hive不支持insert into tablevalues() 這樣的語句。
多表插入
Hive支持以下寫法:
FROM 源表
INSERT OVERWRITE TABLE 目標表1
SELECT 字段1 where條件1
INSERT OVERWRITE TABLE 目標表2
SELECT 字段2 where條件2
Select創建新表
和傳統的關系數據庫類似,Hive支持以下寫法:
CREATE TABLE 新表名
AS
SELECT col1, col2
FROM 源表;
表的修改
ALTER TABLE 表名 RENAME TO新的表名;
ALTER TABLE 表名 ADD COLUMNS (列名列類型);
更新信息請參照官方文檔。
表的刪除
以下語句刪除表的元數據及表數據(外部表只刪除元數據):
Drop table 表名
如果你只是想清空表數據而保留表的定義,你可以直接通過hdfs刪除表目錄下的文件即可。
以下語句將創建一個新表,其結構和已有的表相同,但是表數據為空:
CREATE TABLE 新表 LIKE已有的表;