Hive基础使用

Posted strongmore

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hive基础使用相关的知识,希望对你有一定的参考价值。

Hive的使用方式

可以在Shell命令行下操作Hive,或者使用JDBC代码的方式操作

命令行方式

针对命令行这种方式,其实还有两种使用

  • 第一个是使用bin目录下的hive命令,这个是从hive一开始就支持的使用方式
  • 后来又出现一个beeline命令,它是通过HiveServer2服务连接hive,是一个轻量级的客户端工具,所以后来官方开始推荐使用这个。

具体使用哪个属于个人的一个习惯问题,特别是一些做了很多年大数据开发的人,已经习惯了使用hive命令,如果使用beeline会感觉有点别扭

针对我们写的hive sql通过哪一种客户端去执行结果都是一样的,没有任何区别,所以在这里我们使用哪个就无所谓了。

第一种方式: hive

bin/hive

下面出现以hive开头的内容就是说明我们进入了Hive的命令行中,在这里就可以写Hive的SQL了

show databases; # 默认使用default
show tables; # 默认为空
create table tb_user(id int,name string); # 创建一个表,此时就会向hdfs中创建一个目录

向表里面添加数据,注意,此时就产生了MapReduce任务

insert into tb_user(id,name) values(1,"李四");

hdfs中也会创建一个文件,内容就是表的数据

查询数据,为什么这时没有产生mapreduce任务呢?因为这个计算太简单了,不需要经过mapreduce任务就可以获取到结果,直接读取表对应的数据文件就可以了。

注意,表数据是不支持更新和删除的,只能新增。

可以输入quit退出hive的命令行,或者直接按ctrl+c也可以

第二种方式: beeline

先启动hiveserver2服务

bin/hiveserver2

注意了,启动hiveserver2服务之后,最下面会输出几行Hive Session ID的信息,一定要等到输出4行以后再使用beeline去连接,否则会提示连接拒绝

hiveserver2默认会监听本机的10000端口,所以命令是这样的

bin/beeline -u jdbc:hive2://localhost:10000

创建表报错,提示匿名用户对hdfs目录/user/hive/warehouse没有写权限,解决方法有两个

  1. 给hdfs中的/user/hive/warehouse设置777权限,让匿名用户具备权限 hdfs dfs -chmod -R 777 /user/hive/warehouse
  2. 在启动beeline的时候指定一个对这个目录有操作权限的用户 bin/beeline -u jdbc:hive2://localhost:10000 -n root

这里我们使用第二种。

如果我们没有处理权限的问题,在向表新增数据时,会报以下错误

Error: Error while compiling statement: FAILED: SemanticException 0:0 Expected 2 columns for insclause-0/default@tb_user; select produces 1 columns. Error encountered near token \'\'zs\'\' (state=42000,code=40000)

在工作中我们如果遇到了每天都需要执行的命令,那我肯定想要把具体的执行sql写到脚本中去执行,但是现在这种用法每次都需要开启一个会话,好像还没办法把命令写到脚本中。

注意了,hive后面可以使用 -e 命令,这样这条hive命令就可以放到脚本中定时调度执行了。因为这样每次hive都会开启一个新的会话,执行完毕以后再关闭这个会话。当然了beeline也可以,后面也是跟一个-e参数。

JDBC方式

JDBC这种方式也需要连接hiveserver2服务,前面我们已经启动了hiveserver2服务,在这里直接使用就可以了

<dependency>
    <groupId>org.apache.hive</groupId>
    <artifactId>hive-jdbc</artifactId>
    <version>3.1.2</version>
</dependency>
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * 测试通过jdbc操作hive,如果提示连接被拒绝,那可能是hiveserver2服务没启动
 */
public class TestHiveJdbc 
    public static void main(String[] args) throws SQLException 
        //指定hiveserver2的连接
        String jdbcUrl = "jdbc:hive2://ip:10000";
        //获取jdbc连接,这里的user使用root,就是linux中的用户名,password随便指定即可
        Connection conn = DriverManager.getConnection(jdbcUrl, "root", "any");
        //获取Statement
        Statement stmt = conn.createStatement();
        //指定查询的sql
        String sql = "select * from tb_user";
        //执行sql
        ResultSet res = stmt.executeQuery(sql);
        //循环读取结果
        while (res.next()) 
            System.out.println(res.getInt("id") + "\\t" + res.getString("name"));
        
    

Set命令的使用

在hive命令行中可以使用set命令临时设置一些参数的值,其实就是临时修改hive-site.xml中参数的值

不过通过set命令设置的参数只在当前会话有效,退出重新打开就无效了

如果想要对当前机器上的当前用户有效的话可以把命令配置在 ~/.hiverc文件中

  • 使用set命令配置的参数是当前会话有效
  • 在~/.hiverc文件中配置的是当前机器中的当前用户有效
  • 而在hive-site.xml中配置的则是永久有效了

.hiverc文件格式

set hive.cli.print.current.db = true;
set hive.cli.print.header = true;

Hive的日志配置

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/root/test_hive/hive3.1.2/lib/log4j-slf4j-impl-2.10.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/root/test_hadoop/hadoop3.2/share/hadoop/common/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]

我们每次进入hive命令行的时候都会出现这么几行日志,想要去掉,怎么办呢?
通过分析日志可知,是有重复的日志依赖,所以需要删除一个,
这里是hive中的一个日志依赖包和hadoop中的日志依赖包冲入了,那我们只能去掉Hive的了,因为hadoop是共用的,尽量不要删它里面的东西。为了保险起见,我们可以使用mv给这个日志依赖包重命名,这样它就不生效了

mv log4j-slf4j-impl-2.10.0.jar log4j-slf4j-impl-2.10.0.jar.bak

还有就是当我们遇到Hive执行发生错误的时候,我们要学会去查看Hive的日志信息,通过日志的提示来分析,找到错误的根源,帮助我们及时解决错误。

那我们在哪里查看Hive日志呢,我们可以通过配置文件来找到默认日志文件所在的位置。在hive的conf目录下有一些log4j的模板配置文件,我们需要去修改一下,让它生效。

首先是 hive-log4j2.properties.template 这个文件,去掉 .template 后缀,
修改里面的 property.hive.log.level 和 property.hive.log.dir 参数

property.hive.log.level = WARN
property.hive.root.logger = DRFA
property.hive.log.dir = /root/test_hive/log
property.hive.log.file = hive.log

hive-exec-log4j2.properties.template也是同样的处理。

这样后面分析日志就可以到 /root/test_hive/log 目录下去查看了。

hive学习-----基础语句

上一章介绍了如何安装hive以及hive的基础介绍,这里开始使用hive。使用之前先介绍hive的基础语句的学习,还有什么是内部表、外部表。

hive基础语句

我们来看看最基本的格式,因为格式有很多种,我们先来看一个总的,然后一点点解析。

1.建表语句


CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name
  // 定义字段名,字段类型
  [(col_name data_type [COMMENT col_comment], ...)]
  // 给表加上注解
  [COMMENT table_comment]
  // 分区
  [PARTITIONED BY (col_name data_type [COMMENT col_comment], ...)]
  // 分桶
  [CLUSTERED BY (col_name, col_name, ...) 
  // 设置排序字段 升序、降序
  [SORTED BY (col_name [ASC|DESC], ...)] INTO num_buckets BUCKETS]
  [
  	// 指定设置行、列分隔符 
   [ROW FORMAT row_format] 
   // 指定Hive储存格式:textFile、rcFile、SequenceFile 默认为:textFile
   [STORED AS file_format]
   
   | STORED BY 'storage.handler.class.name' [ WITH SERDEPROPERTIES (...) ]  (Note:  only available starting with 0.6.0)
  ]
  // 指定储存位置
  [LOCATION hdfs_path]
  // 跟外部表配合使用,比如:映射HBase表,然后可以使用HQL对hbase数据进行查询,当然速度比较慢
  [TBLPROPERTIES (property_name=property_value, ...)]  (Note:  only available starting with 0.6.0)
  [AS select_statement]  (Note: this feature is only available starting with 0.5.0.)

我们来分开一个一个细说:

建表1:全部使用默认建表方式

这里的是最基本的默认的建表语句,最后一句也可以没有,但是意味着你只能有一列数据。

create table students
(
    id bigint,
    name string,
    age int,
    gender string,
    clazz string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','; // 必选,指定列分隔符 

建表2:指定location (这种方式也比较常用)

一般在数据已经上传到HDFS,想要直接使用,会指定Location,通常Locaion会跟外部表一起使用,内部表一般使用默认的location
指定数据的存储位置不一定是外部表,后面会说

create table students2
(
    id bigint,
    name string,
    age int,
    gender string,
    clazz string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
LOCATION '/input1'; // 指定Hive表的数据的存储位置

建表3:指定存储格式

这个是指定表的存储格式,对于这个存储格式,在上一章节介绍过数据存储的几种格式。

create table students3
(
    id bigint,
    name string,
    age int,
    gender string,
    clazz string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
STORED AS rcfile; // 指定储存格式为rcfile,inputFormat:RCFileInputFormat,outputFormat:RCFileOutputFormat,如果不指定,默认为textfile,注意:除textfile以外,其他的存储格式的数据都不能直接加载,需要使用从表加载的方式。

建表4

create table xxxx as select_statement(SQL语句) (这种方式比较常用)

这种建表方式是不是很熟悉?在mysql中我们也接触过。

create table students4 as select * from students2;

建表5

create table xxxx like table_name  只想建表,不需要加载数据
create table students5 like students;

hive加载数据

上传文件

  1. 使用 hdfs dfs -put ‘本地数据’ 'hive表对应的HDFS目录下
  2. 使用 load data inpath
第一种方式应该就不用说了,这里来看第二中方式
// 将HDFS上的/input1目录下面的数据 移动至 students表对应的HDFS目录下,注意是 移动、移动、移动
load data inpath '/input1/students.txt' into table students;
这里为什么要强调移动呢,因为这种方式传文件,原来目录下的文件就不存在了。但是通过第一种的方式进行上传文件,原路径的问价还是存在的。这里简单区分一下。
这里注意一下:
  我们如果上传数据到hdfs的目录和hive表没有关系。
  上传到hive表和hive表有关系(需要进行数据的转换)。

清空文件

清空表内容

truncate table students;

这里注意一下:加上 local 关键字 可以将Linux本地目录下的文件 上传到 hive表对应HDFS 目录下 原文件不会被删除

load data local inpath '/usr/local/soft/data/students.txt' into table students;
overwrite 覆盖加载
load data local inpath '/usr/local/soft/data/students.txt' overwrite into table students;

加载数据

基础格式

hive> dfs -put /usr/local/soft/data/students.txt /input2/;
hive> dfs -put /usr/local/soft/data/students.txt /input3/;

删除表

格式

drop table <table_name>
Moved: 'hdfs://master:9000/input2' to trash at: hdfs://master:9000/user/root/.Trash/Current

OK
Time taken: 0.474 seconds
hive> drop table students_external;
OK
Time taken: 0.09 seconds
hive> 

注意:

  1. 可以看出,删除内部表的时候,表中的数据(HDFS上的文件)会被同表的元数据一起删除。
  2. 删除外部表的时候,只会删除的元数据,不会删除表中的数据(HDFS上的文件)。
  3. 一般在公司中,使用外部表多一点,因为数据可以需要多个程序使用,避免误删,通常外部表结合location一起使用。
  4. 外部表还可以将其他数据数据源中的数据,映射到hive中,比如说:hbase,ElasticSearch…
  5. 设计外部表的初衷就是 让 表的元数据 与 数据 解耦。

Hive 分区

概念

分区表实际上就是在表的目录下在以分区命名,建子目录

作用

进行分区裁剪,避免全表扫描,减少MapReduce处理的数据量,提高效率

注意事项:

  1. 一般在公司中,几乎所有的表都是分区表,通常按日期分区、地域分区。
  2. 分区表在使用的时候记得加上分区字段
  3. 分区也不是越多越好,一般不超过三级,按实际业务衡量。

建立分区表

create external table students_pt1
(
    id bigint,
    name string,
    age int,
    gender string,
    clazz string
)
PARTITIONED BY(pt string)
ROW F

增加一个分区

alter table students_pt1 add partition(pt='20210904');

删除一个分区

alter table students_pt drop partition(pt='20210904');

查看分区

查看某个表中的所有分区

show partitions students_pt; // 推荐这种方式(直接从元数据中获取分区信息)

select distinct pt from students_pt; // 不推荐

插入数据

往分区中添加数据

insert into table students_pt partition(pt='20210902') select * from students;

load data local inpath '/usr/local/soft/data/students.txt' into table students_pt partition(pt='20210902');

查询分区数据

查询某个分区的数据

// 全表扫描,不推荐,效率低
select count(*) from students_pt;

// 使用where条件进行分区裁剪,避免了全表扫描,效率高
select count(*) from students_pt where pt='20210101';

// 也可以在where条件中使用非等值判断
select count(*) from students_pt where pt<='20210112' and pt>='20210110';

Hive动态分区

有时候我们原始表中的的数据包含了''日期字段 dt'',我们需要根据dt
中不同的日期,分为不同的分区,将原始表改成分区表。
hive默认不开启动态分区

概念

动态分区:根据数据中某几列的不同的取值 划分 不同的数据。  

开启Hive的动态分区支持

# 表示开启动态分区
hive> set hive.exec.dynamic.partition=true;
# 表示动态分区模式:strict(需要配合静态分区一起使用)、nostrict
# strict: insert into table students_pt partition(dt='anhui',pt) select ......,pt from students;
hive> set hive.exec.dynamic.partition.mode=nostrict;
# 表示支持的最大的分区数量为1000,可以根据业务自己调整
hive> set hive.exec.max.dynamic.partitions.pernode=1000;

练习

  1. 建立原始表并加载数据
create table students_dt
(
    id bigint,
    name string,
    age int,
    gender string,
    clazz string,
    dt string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';

  1. 建立分区表并加载数据
create table students_dt_p
(
    id bigint,
    name string,
    age int,
    gender string,
    clazz string
)
PARTITIONED BY(dt string)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';
  1. 使用动态分区插入数据
// 分区字段需要放在 select 的最后,如果有多个分区字段 同理,它是按位置匹配,不是按名字匹配
insert into table students_dt_p partition(dt) select id,name,age,gender,clazz,dt from students_dt;


// 比如下面这条语句会使用age作为分区字段,而不会使用student_dt中的dt作为分区字段
insert into table students_dt_p partition(dt) select id,name,age,gender,dt,age from students_dt;
  1. 多级分区
create table students_year_month
(
    id bigint,
    name string,
    age int,
    gender string,
    clazz string,
    year string,
    month string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';

create table students_year_month_pt
(
    id bigint,
    name string,
    age int,
    gender string,
    clazz string
)
PARTITIONED BY(year string,month string)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';

insert into table students_year_month_pt partition(year,month) select id,name,age,gender,clazz,year,month from students_year_month;

Hive分桶

概念

hive的分桶世纪上就是对文件(数据)的进一步切分
hive默认是关闭分桶

作用

在往分桶表中插入数据的时候,会根据clustered by 指定的字段,进
行hash分区,对指定的buckets个数,进行取余,进而可以将数据分割
成buckets个数个文件,进以达到数据均匀分布,可以解决Map的“数据
倾斜”问题,方便我们取抽样数据,提高Map join效率。

分桶字段需要根据业务进行设定

开启分桶

由于分桶默认是关闭的,

hive> set hive.enforce.bucketing=true;

建立分桶表

create table students_buks
(
    id bigint,
    name string,
    age int,
    gender string,
    clazz string
)
CLUSTERED BY (clazz) into 12 BUCKETS
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','; 

添加数据

// 直接使用load data 并不能将数据打散
load data local inpath '/usr/local/soft/data/students.txt' into table students_buks;

// 需要使用下面这种方式插入数据,才能使分桶表真正发挥作用
insert into students_buks select * from students;

https://zhuanlan.zhihu.com/p/93728864 Hive分桶表的使用场景以及优缺点分析

Hive JDBC

启动hive server2

hive --service hiveserver2 &

或者
hiveserver2 &
新建maven项目并添加两个依赖
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-common</artifactId>
        <version>2.7.6</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.hive/hive-jdbc -->
    <dependency>
        <groupId>org.apache.hive</groupId>
        <artifactId>hive-jdbc</artifactId>
        <version>1.2.1</version>
    </dependency>
编写JDBC代码
import java.sql.*;

public class HiveJDBC 
    public static void main(String[] args) throws ClassNotFoundException, SQLException 
        Class.forName("org.apache.hive.jdbc.HiveDriver");
        Connection conn = DriverManager.getConnection("jdbc:hive2://master:10000/test3");
        Statement stat = conn.createStatement();
        ResultSet rs = stat.executeQuery("select * from students limit 10");
        while (rs.next()) 
            int id = rs.getInt(1);
            String name = rs.getString(2);
            int age = rs.getInt(3);
            String gender = rs.getString(4);
            String clazz = rs.getString(5);
            System.out.println(id + "," + name + "," + age + "," + gender + "," + clazz);
        
        rs.close();
        stat.close();
        conn.close();
    

以上是关于Hive基础使用的主要内容,如果未能解决你的问题,请参考以下文章

hive学习-----基础语句

HIVE基础操作

hive基础

Hive基础知识

Hive基础

Hive知识总结