Hive配置运行及表的操作

jopen 10年前

配置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已有的表;