9. Hive使用

1.Hive数据导入的六种类型:

以下面两个表来实验:

create table emp(
empno int,
ename string,
job string,
mgr int,
hiredate string,
sal double,
comm double,
deptno int)
partitioned by (dt string)
row format delimited
fields terminated by '\t';

create external table dept (
deptno int,
dname string,
loc string)
partitioned by (dt string)
row format delimited 
fields terminated by '\t';

1.从本地文件系统导入到hive表中:

使用load data语句可以直接导入本地文件到hive表中,加关键字local表示从本地系统上传文件到hive表:

hive> load data local inpath '/home/natty.ma/bigdata/hadoop/files/emp.txt' overwrite into table testdb.emp1;

2.从HDFS导入文件到hive表中:

如果不加关键字local表示从hdfs加载文件到hive表。下面先上传本地文件到hdfs,再加载到hive表:

$bin/hdfs dfs -put /home/natty.ma/bigdata/hadoop/files/emp.txt /user/natty.ma
hive> load data inpath '/user/natty.ma/emp.txt' overwrite into table testdb.emp partition (dt='20170228');

load语句实际上是 移动文件

3.加载数据覆盖表中已有的数据:

上边2个语句中,load data语句,OVERWRITE参数决定加载文件时是否覆盖。

4.创建表时通过select语句加载:

hive> create table testdb.emp2 as select * from testdb.emp1;

create table ... as 语句会走mapreduce。

5.创建表,通过insert语句加载:

hive> insert into table testdb.emp3 select * from testdb.emp2;

可以增加overwrite选项,来选择insert时,是否覆盖原表的数据。如果不加overwrite参数再执行一次该语句,那么emp3表的数据将会翻倍,查看该表hdfs的目录,会发现有2个文件(而不是一个文件):

$bin/hdfs dfs -ls /user/hive/warehouse/testdb.db/emp3;
Found 2 items
-rwxrwxrwx   2 root supergroup        661 2017-02-28 10:55 /user/hive/warehouse/testdb.db/emp3/000000_0
-rwxrwxrwx   2 root supergroup        661 2017-02-28 10:57 /user/hive/warehouse/testdb.db/emp3/000000_0_copy_1

6.创建表的时候通过Location指定:

先创建一张表,并指定该表的LOCATION,之后往LOCATON目录(HDFS上的路径)上传数据文件,再查询表时,就可以看到数据了。

create table emp4(
empno int,
ename string,
job string,
mgr int,
hiredate string,
sal double,
comm double,
deptno int)
row format delimited
fields terminated by '\t'
LOCATION '/user/natty.ma/hive_test/emp';

$bin/hdfs dfs -put /home/natty.ma/bigdata/hadoop/files/emp.txt /user/natty.ma/hive_test/emp

PS: 我在实验时发现,如果先上传文件,再创建一个表,LOCATION目录是上传的HDFS目录的话,上传的目录会被覆盖清空。

2.Hive导出数据的几种方式:

1.使用Insert语句导出到本地(或HDFS):

通过LOCAL关键字来确定输出到本地或hdfs。

hive> INSERT OVERWRITE LOCAL DIRECTORY '/home/natty.ma/bigdata/hadoop/output' ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' select * from testdb.emp4;
hive> insert overwrite directory '/user/natty.ma/hive_test/output' select * from testdb.emp4;

需要注意的是,在导出到Linux本地目录后,需要cd到上一级目录,再进入该目录才能看到导出的文件。

2.hadoop命令导出:

使用get命令,直接将HDFS上的文件导出到Linux本地系统中。

hive> dfs -get /user/natty.ma/hive_test/emp/* /home/natty.ma/bigdata/hadoop/output/;

3.使用 hive -e "SQL" > xx.txt方式导出文件:

hive -e可以执行执行Hive SQL,将结果导出到文件即可。

hive -e "select * from testdb.emp4" > /home/natty.ma/bigdata/hadoop/output/output.txt

4.使用 sqoop导出文件:

sqoop的使用在后边会介绍。

3.设置 reduce task数目和排序分析:

hive中有4种主要的排序方式:

  • order by : 全局排序,对所有数据进行排序。
  • sort by :局部排序,对每个reduce中的数据进行排序,但是全局不排序。
  • distribute by:按照某个字段来分区,同一个分区的数据交给一个reduce来处理。
  • cluster by:是distribute by ... sort by ...的组合,但是不同于上边两项,distribute by 和sort by必须是同一项。

在hive中,可以设置执行作业的reduce的数量:

set mapreduce.job.reduces = <NUMBER>

同时,reduce的数量,也决定了结果输出的文件的个数。

hive> set mapreduce.job.reduces = 3;
hive> insert overwrite local directory '/home/natty.ma/bigdata/hadoop/output/' select * from testdb.emp4 distribute by deptno sort by empno;

上边语句会直接输出3个文件。如果set成5的话,当然会输出5个文件。
我在实验过程中,也发现了一个文件,如果执行的select语句,不需要进行reduce阶段。例如select * 或者select 部分字段,就只会生成一个文件了。

4.设置哪些HQL执行MapReduce:

在执行HQL语句时,我们注意到,有些HQL需要执行MapReduce,而有些则不需要。对于是否执行MapReduce可以进行配置。可以在hive-site.xml或者通过set设置。

<property>
    <name>hive.fetch.task.conversion</name>
    <value>more</value>
    <description>
      Expects one of [none, minimal, more].
      Some select queries can be converted to single FETCH task minimizing latency.
      Currently the query should be single sourced not having any subquery and should not have
      any aggregations or distincts (which incurs RS), lateral views and joins.
      0. none : disable hive.fetch.task.conversion
      1. minimal : SELECT STAR, FILTER on partition columns, LIMIT only
      2. more    : SELECT, FILTER, LIMIT only (support TABLESAMPLE and virtual columns)
    </description>
  </property>

可以有minimal和more来进行选择,首先,可以不进行MR的查询必须是单库查询、不含有任何子查询、不包含聚合函数、distinct、视图、关联等。

1. minimal:

SELECT * , 只筛选partition字段, LIMIT子句。
那么类似的语句不会执行MapReduce:

hive> select * from testdb.emp where dt>='20170227' limit 10;

但是 select empno from testdb.emp where dt>='20170227' limit 10; 会执行MapReduce。
PS: 在执行此sql时,报了错误: org.apache.hadoop.hive.ql.ppd.ExprWalkerInfo.getConvertedNode
经查证hive jira,这是hive的一个bug,需要做如下设置,可以避免错误:

hive> set hive.optimize.ppd=false;

2. more :

如果配置的是more的模式,相比于minimal,如果只select部分字段也不会走MR。

5.Hiveserver2的使用:

Hiverserver(Thrift Hive Server)是老版本的,为了解决同时响应多客户端请求的问题,提供了升级的Hiveserver2版本。

首先,Hive是一个访问HDFS的客户端。但是Hive也是可以实现C/S结构的,Hiveserver2服务开启后,就可以实现Server的功能,可以响应其他(Hive Client端)的请求。那么其他客户端可以连接Hiveserver2服务的Server。

[官方 hiveserver2介绍][1]
[1]:https://cwiki.apache.org/confluence/display/Hive/Setting+Up+HiveServer2

1. 配置hive-site.xml:

启动hiveserver2 服务,需要配置这两项(其余两项默认值即可):

  • hive.server2.thrift.port
  • hive.server2.thrift.bind.host
<property>
    <name>hive.server2.thrift.port</name>
    <value>10000</value>
    <description>Port number of HiveServer2 Thrift interface when hive.server2.transport.mode is 'binary'.</description>
</property>
<property>
    <name>hive.server2.thrift.bind.host</name>
    <value>hadoop-senior01.pmpa.com </value>
    <description>Bind host on which to run the HiveServer2 Thrift service.    </description>
</property>

2. 启动hiveserver2服务:

注意以后台运行方式启动hiveserver2,启动后监控10000端口是否在监听来判断是否启动成功了。

$ hive --service hiveserver2 &
$ netstat -antp | grep 10000

3. 客户端连接hiveserver:

可以以两种方式连接hiveserver: JDBC和Beeline 。
(1)Beeline方式(基于 SQLLine CLI的JDBC客户端):
下面是以Beeline命令行方式登录:

$ bin/beeline 
beeline> help
beeline> !connect jdbc:hive2://hd-master:10000 root 123456
0: jdbc:hive2://hd-master:10000> show databases;
0: jdbc:hive2://hd-master:10000> select * from testdb.emp4;

beeline的命令在执行时,需要在前边加上!,help命令可以查看具体详细用法。connect连接串的格式 jdbc:hive2://主机名(ip):端口号 用户名 密码
用户名和密码是搭建hadoop hive环境的用户名和其密码。

(2)JDBC方式访问:
使用JDBC方式访问hive,classpath需要包含以下jar包:

  • hadoop-common*.jar
  • $HIVE_HOME/lib/*.jar

下面一个例子,访问hive中的dept表,并提供了执行更新语句的方式:

package com.pmpa.hiveserver.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;

import java.sql.Statement;
import java.util.ArrayList;;

public class HiveJDBC {
    
    private static String jdbcdriver = "org.apache.hive.jdbc.HiveDriver";
    
    public static void main(String[] args) {
        
        ArrayList<Dept> depts = new ArrayList<Dept>();
        
        try {
            Class.forName(jdbcdriver);
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.exit(1);
        }
        
        try {
            Connection con = 
                DriverManager.getConnection("jdbc:hive2://localhost:10000/testdb","root", "123456");
            Statement stmt = con.createStatement();
            
            //无返回结果的更新元数据的语句。
            String drop_str = "drop table if exists jdbc_test";
            stmt.execute(drop_str);
            String create_str = "create table jdbc_test (id int, name string, url string)";
            stmt.execute(create_str);
            
            //有返回结果集的查询语句
            String query_str = "select * from dept";
            System.out.println("Running: " + query_str);
            ResultSet rs = stmt.executeQuery(query_str);
            while(rs.next())
            {
                depts.add(new Dept(rs.getInt(1),rs.getString(2)
                        ,rs.getString(3),rs.getString(4)));
            }
            System.out.println("Complete Running: " + query_str);
            
            System.out.print(depts);
            
            
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

其中,Dept是hive表testdb.dept的POJO类。
为了加载classpath,使用下面的shell来配置classpath:

#!/bin/bash
for i in `ls ${HADOOP_HOME}/share/hadoop/common/*.jar`
do
  CLASSPATH=${i}:${CLASSPATH}
done
for i in `ls ${HIVE_HOME}/lib/*.jar`; do
  CLASSPATH=${i}:${CLASSPATH}
done
java -cp ${CLASSPATH}  com.pmpa.hiveserver.jdbc.HiveJDBC

PS:需要注意如果打成jar包,不能使用 java -cp ${classpath} -jar xx.jar xxx 方式,这样的话无法加载classpath。

java -classpath some.jar -jar test.jar
这种方式是不行的,因为使用classpath指定的jar是由AppClassloader来加载,java 命令 加了-jar 参数以后,AppClassloader就只关注test.jar范围内的class了,classpath参数失效。

6. Hive UDF开发:

1. UDF介绍

除了Hive中提供的一些基本函数min、max等等外。还可以自行开发定义函数UDF。UDF函数可以直接应用于select语句。需要特别注意以下几点:

  • 自定义UDF需要继承类org.apache.hadoop.hive.ql.exec.UDF
  • 需要实现evaluate()方法,该方法可以重载,不同的重载方法,对应着该函数不同参数用法。
  • UDF必须要有返回类型,也就是evaluate()方法,可以返回null值,但是该方法不可以是void的。
  • 建议使用hadoop中定义的数据类型,例如Text、LongWritable等,不建议使用java数据类型。

2. UDF开发

下面以一个简单的函数为例,展示Hive UDF的开发过程。
函数功能: 输入20170312 返回值 '2017-03-12'。下面是详细步骤:
(1)开发GeneralTest类:

package com.pmpa.hiveserver.udf;

import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;

public class GeneralTest extends UDF {  
    private Text out = new Text();
    public Text evaluate(LongWritable date_int){
        String input = date_int.toString();
        String output = input.substring(0,4) + "-" + input.substring(4,6) + "-" 
                + input.substring(6,8) ;
        out.set(output);
        return out;
    }
}

(2)java程序打成jar包,并发布到目标机器上。
上传到目录:/home/natty.ma/bigdata/hadoop/javaDev/GeneralTest.jar
(3)在hive客户端添加jar包:

hive> add jar /home/natty.ma/bigdata/hadoop/javaDev/GeneralTest.jar;

(4)创建临时函数:

hive> CREATE TEMPORARY FUNCTION general_test_mn AS 'com.pmpa.hiveserver.udf.GeneralTest';

(5)函数测试:

hive> SELECT general_test_mn(20160203);
OK
2016-02-03

3. UDF类型

UDF只能实现“一进一出”操作,如果要“多进一出”需要实现UDAF。 所谓一进一出,是输入一行,输出一行,例如上边的例子。多进一出就是所谓的聚合函数。将多行输入记录,聚合输出一行。类似于sum()。

UDF包含以下类型:

  • UDF: User Defined Function , 一进一出。
  • UDAF: User Defined Aggregation Function , 多进一出,聚合函数。 例如sum,count。
  • UDTF: User Defined Table Function, 一进多出,例如lateral view explore()
    下面的文章比较清晰地介绍了这几种UDF的开发方法:

4. UDF开发(继承GenericUDF)

5. UDTF开发:

开发udtf,需要继承类:org.apache.hadoop.hive.ql.udf.generic.GenericUDTF。
为了更好地理解UDTF,可以参考hive内置的一个UDTF,即explode、json_tuple函数等。
需要重载3个方法

// in this method we specify input and output parameters: input ObjectInspector and an output struct
abstract StructObjectInspector initialize(ObjectInspector[] args) throws UDFArgumentException; 

// here we process an input record and write out any resulting records 
abstract void process(Object[] record) throws HiveException;

// this function is Called to notify the UDTF that there are no more rows to process. Clean up code or additional output can be produced here.
abstract void close() throws HiveException;

initialize()方法指定输入和输出参数:输入参数,ObjectInspector;输出参数:struct。
process()方法处理输入记录,并生成结果记录。
close()方法表示没有记录需要处理。

6. UDAF开发:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容