深入浅出学习Hive

Posted 刘元涛

tags:

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

本文是基于CentOS 7.9系统环境,进行hive的学习和使用

一、Hive的简介

1.1 Hive基本概念

(1) 什么是hive

Hive是用于解决海量结构化日志的数据统计工具,是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张表,并提供类SQL查询功能
(2) Hive的本质

Hive的本质就是将HQL转化成MapReduce程序

1.2 Hive优缺点

(1) 优点

  1. 操作接口采用类SQL语法,提供快速开发的能力(简单、容易)
  2. 避免写MapReduce程序,减少开发人员的学习成本
  3. Hive优势在于处理大数据,常用于数据分析,适用于实时性要求不高的场景
  4. hive支持用户自定义函数,用户可以根据自己的需求来实现自己的函数

(2) 缺点

  1. Hive执行延迟比较高,对于处理小数据没有优势
  2. hive的HQL表达能力有限(迭代式算法无法表达;数据挖掘方面不擅长,由于MapReduce数据处理流程的限制,效率更高的算法却无法实现)
  3. hive的效率比较低(hive自动生成的MapReduce作业,通常情况下不够智能化;hive调优比较困难,粒度较粗)

1.3 Hive架构

  • Client 用户接口

    CLI(command-line interface)、JDBC/ODBC(jdbc访问hive)、WEBUI(浏览器访问hive)

  • Metastore

    元数据包括:表名、表所属的数据库(默认是default)、表的拥有者、列/分区字段、表的类型(是否是外部表)、表的数据所在目录等;默认存储在自带的derby数据库中,推荐使用mysql存储Metastore

  • SQL Parser 解析器

    对SQL语句进行解析,转换成抽象语法树AST,并进行语法分析和检查

  • Physical Plan 编译器

    将抽象语法树AST编译成逻辑执行计划

  • Query Optimizer 优化器

    对逻辑执行计划进行优化

  • Execution 执行器

    将逻辑执行计划转换成可以运行的物理计划,也就是MR任务

1.4 Hive工作机制

Hive通过给用户提供的一系列交互接口,接收到用户的指令(SQL),使用自己的Driver,结合元数据(MetaStore),将这些指令翻译成MapReduce,提交到Hadoop中执行,最后,将执行返回的结果输出到用户交互接口。

1.5 Hive和数据库比较

由于 Hive 采用了类似SQL 的查询语言 HQL(Hive Query Language),因此很容易将 Hive 理解为数据库。其实从结构上来看,Hive 和数据库除了拥有类似的查询语言,再无类似之处。本文将从多个方面来阐述 Hive 和数据库的差异。数据库可以用在 Online 的应用中,但是Hive 是为数据仓库而设计的,清楚这一点,有助于从应用角度理解 Hive 的特性。

  • 查询语言

    由于SQL被广泛的应用在数据仓库中,因此,专门针对Hive的特性设计了类SQL的查询语言HQL。熟悉SQL开发的开发者可以很方便的使用Hive进行开发。

  • 数据存储位置

    Hive 是建立在 Hadoop 之上的,所有 Hive 的数据都是存储在 HDFS 中的。而数据库则可以将数据保存在块设备或者本地文件系统中。

  • 数据更新

    由于Hive是针对数据仓库应用设计的,而数据仓库的内容是读多写少的。因此,Hive中不建议对数据的改写,所有的数据都是在加载的时候确定好的。而数据库中的数据通常是需要经常进行修改的,因此可以使用 INSERT INTO … VALUES 添加数据,使用 UPDATE … SET修改数据。

  • 执行

    Hive中大多数查询的执行是通过 Hadoop 提供的 MapReduce 来实现的。而数据库通常有自己的执行引擎。

  • 执行延迟

    Hive 在查询数据的时候,由于没有索引,需要扫描整个表,因此延迟较高。另外一个导致 Hive 执行延迟高的因素是 MapReduce框架。由于MapReduce 本身具有较高的延迟,因此在利用MapReduce 执行Hive查询时,也会有较高的延迟。相对的,数据库的执行延迟较低。当然,这个低是有条件的,即数据规模较小,当数据规模大到超过数据库的处理能力的时候,Hive的并行计算显然能体现出优势。

  • 可扩展性

    由于Hive是建立在Hadoop之上的,因此Hive的可扩展性是和Hadoop的可扩展性是一致的(世界上最大的Hadoop 集群在 Yahoo!,2009年的规模在4000 台节点左右)。而数据库由于 ACID 语义的严格限制,扩展行非常有限。目前最先进的并行数据库 Oracle 在理论上的扩展能力也只有100台左右。

  • 数据规模

    由于Hive建立在集群上并可以利用MapReduce进行并行计算,因此可以支持很大规模的数据;对应的,数据库可以支持的数据规模较小。

二、Hive的安装

2.1 Hive下载

apache-hive-1.2.1-bin.tar.gz


2.2 Hive解压

tar -xzvf apache-hive-1.2.1-bin.tar.gz -C /opt/module
cd /opt/module
mv apache-hive-1.2.1-bin hive


2.3 配置环境变量

vi /etc/profile
# 添加如下内容
#HIVE_HOME
export HIVE_HOME=/opt/module/hive
export PATH=$PATH:$HIVE_HOME/bin


2.4 修改hive配置文件

cd /opt/module/hive/conf
cp hive-env.sh.template hive-env.sh
vi hive-env.sh
# 添加如下内容
# Set HADOOP_HOME to point to a specific hadoop install directory
HADOOP_HOME=/opt/module/hadoop-2.7.2
# Hive Configuration Directory can be controlled by:
export HIVE_CONF_DIR=/opt/module/hive/conf


2.5 启动并测试hive

hive


# 创建数据库
create database test;
# 创建数据表
create table student(id int, name string);
# 插入数据
insert into table student values(1001, "zhangsan");
# 查询数据
select * from student;
# 删除数据表
drop table student;
# 删除数据库
drop database test;


2.6 hive的bug

hive默认存储元数据的数据库为derby,不支持并发访问,多开几个hive客户端会出现异常

2.7 MySQL的安装

hive默认存储元数据的数据库为derby,不支持并发访问,多开几个hive客户端会出现异常,因此需要安装MySQL数据库来替换

CentOS 7离线安装MySQL 5.6

2.8 Hive配置MySQL

cd /opt/module/hive/conf
vi hive-site.xml
# 添加如下内容
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
    <property>
      <name>javax.jdo.option.ConnectionURL</name>
      <value>jdbc:mysql://192.168.1.101:3306/metastore?createDatabaseIfNotExist=true</value>
      <description>JDBC connect string for a JDBC metastore</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>root</value>
      <description>username to use against metastore database</description>
    </property>

    <property>
      <name>javax.jdo.option.ConnectionPassword</name>
      <value>123456</value>
      <description>password to use against metastore database</description>
    </property>
</configuration>


2.9 启动Hive

hive


2.10 Beeline启动Hive

cd /opt/module/hive/bin
./hiveserver2


打开另一个终端

cd /opt/module/hive/bin
./beeline
!connect jdbc:hive2://hadoop101:10000


只需输入启动hadoop的用户名,不需要密码

三、Hive的使用

3.1 Hive的交互命令

运行来自命令行的SQL

cd /opt/module/hive
bin/hive -e "select * from test.student;"
bin/hive -e "select * from test.student;">result.log


运行来自文件的SQL

cd /opt/module/hive
vi test.sql
# 添加如下内容
select * from test.student;
# 执行下面命令
bin/hive -f test.sql>result.log


退出hive客户端

quit;


3.2 Hive数据仓库位置配置

cd /opt/module/hive/conf
vi hive-site.xml
# 添加如下内容
<property>
    <name>hive.metastore.warehouse.dir</name>
    <value>/user/hive/warehouse</value>
    <description>location of default database for the warehouse</description>
</property>


3.3 查询后信息显示配置(可选配)

cd /opt/module/hive/conf
vi hive-site.xml
# 添加如下内容
<property>
    <name>hive.cli.print.header</name>
    <value>true</value>
</property>
<property>
    <name>hive.cli.print.current.db</name>
    <value>true</value>
</property>


3.4 Hive运行日志信息配置(必须配置)

cd /opt/module/hive/conf
cp hive-log4j.properties.template hive-log4j.properties
vi hive-log4j.properties
# 添加如下内容
hive.log.dir=/opt/module/hive/logs


四、Hive的数据类型

4.1 Hive基本数据类型

hive数据类型java数据类型长度示例
tinyintbyte1byte有符号整数20
smalintshort2byte有符号整数20
intint4byte有符号整数20
bigintlong8byte有符号整数20
booleanboolean布尔类型,true或者falseTRUE FALSE
floatfloat单精度浮点数3.14159
doubledouble双精度浮点数3.14159
stringstring字符系列,可以使用单引号或者双引号‘now is’ “i am a”
timestamp 时间类型 
binary 字节数组 

4.2 Hive集合数据类型

数据类型描述语法示例
struct和c语言中的struct类似,都可以通过“点”符号访问元素内容。例如,如果某个列的数据类型是STRUCTfirst STRING, last STRING,那么第1个元素可以通过字段.first来引用。struct() 例如struct<street:string, city:string>
mapMAP是一组键-值对元组集合,使用数组表示法可以访问数据。例如,如果某个列的数据类型是MAP,其中键->值对是’first’->’John’和’last’->’Doe’,那么可以通过字段名[‘last’]获取最后一个元素map() 例如map<string, int>
array数组是一组具有相同类型和名称的变量的集合。这些变量称为数组的元素,每个数组元素都有一个编号,编号从零开始。例如,数组值为[‘John’, ‘Doe’],那么第2个元素可以通过数组名[1]进行引用。Array() 例如array
  • 案例

    创建数据文件test.txt

vi test.txt
# 添加如下内容
songsong,bingbing_lili,xiao song:18_xiaoxiao song:19,hui long guan_beijing
yangyang,caicai_susu,xiao yang:18_xiaoxiao yang:19,chao yang_beijing


创建表结构文件test.sql

vi test.sql
# 添加如下内容
create table test.test(
name string,
friends array<string>,
children map<string, int>,
address struct<street:string, city:string>
)
row format delimited fields terminated by ','
collection items terminated by '_'
map keys terminated by ':'
lines terminated by '\\n';


上传数据文件test.txt

hdfs dfs -put test.txt /user/hive/warehouse/test.db/test


测试查询

hive
use test;
select name,friends[1],children["xiao song"],address.city from test;


4.3 类型转化

Hive的原子数据类型是可以进行隐式转换的,类似于Java的类型转换,例如某表达式使用INT类型,TINYINT会自动转换为INT类型,但是Hive不会进行反向转化,例如,某表达式使用TINYINT类型,INT不会自动转换为TINYINT类型,它会返回错误,除非使用CAST操作。

  • 隐式类型转换规则如下
  1. 任何整数类型都可以隐式地转换为一个范围更广的类型,如TINYINT可以转换成INT,INT可以转换成BIGINT。
  2. 所有整数类型、FLOAT和STRING类型都可以隐式地转换成DOUBLE。
  3. TINYINT、SMALLINT、INT都可以转换为FLOAT。
  4. BOOLEAN类型不可以转换为任何其它的类型。
  • 可以使用CAST操作显示进行数据类型转换

    例如CAST(‘1’ AS INT)将把字符串’1’ 转换成整数1;如果强制类型转换失败,如执行CAST(‘X’ AS INT),表达式返回空值 NULL。

select '1'+2, cast('1'as int) + 2;


五、DDL数据库定义语言

5.1 创建数据库

CREATE DATABASE [IF NOT EXISTS] database_name
[COMMENT database_comment]
[LOCATION hdfs_path]
[WITH DBPROPERTIES (property_name=property_value, ...)];


  • 实例:新建一个名为test1的数据库,存储在HDFS中的 /test 路径下
create database test1 comment "test1 database" location "/test" with dbproperties("zhangsan"="lisi");


5.2 显示所有数据库

show databases;


5.3 过滤显示数据库

show databases like 'test';


5.4 显示指定数据库的信息

desc database test1;


5.5 显示指定数据库的详细信息

desc database extended test1;


5.6 切换数据库

use test1;


5.7 修改数据库

alter database test1 set dbproperties('name'='zhangsan');


5.8 删除空的数据库

drop database test1;
drop database if exists test1;


5.9 删除非空数据库

drop database test1 cascade;


5.10 创建数据表

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], ...)] ## 分区表在HDFS中体现为分为多个文件夹
[CLUSTERED BY (col_name, col_name, ...) ## 分区表在HDFS中体现为一个文件夹下分为多个文件
[SORTED BY (col_name [ASC|DESC], ...)] INTO num_buckets BUCKETS]
[ROW FORMAT row_format]
[STORED AS file_format]
[LOCATION hdfs_path]
[TBLPROPERTIES (property_name=property_value, ...)]
[AS select_statement]


  • 实例:新建一个名为student1的表,存储在HDFS中的 /student 路径下
create table student1(id int comment "Identity", age int comment "Age") comment "Student" row format delimited fields terminated by '\\t' location '/student';


5.11 显示指定表的信息和创建表时的配置

desc student1;
show create table student1;


5.12 显示指定表的详细信息

desc formatted student1;


5.13 外部表和管理表

  • 管理表(managed_table)

删除表后,存储在HDFS上的数据也会被删除

  • 外部表(external_table)

删除表后,存储在HDFS上的数据不会被删除

5.14 将数据表修改为外部表

alter table student1 set tblproperties('EXTERNAL'='TRUE');


5.15 将数据表修改为管理表

alter table student2 set tblproperties('EXTERNAL'='FALSE');


5.16 创建分区表

谓词下退,先走过滤
1. 新建表

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


2. 加载数据

hive (default)> load data local inpath '/opt/module/datas/dept.txt' into table default.dept_partition partition(month='201709');
hive (default)> load data local inpath '/opt/module/datas/dept.txt' into table default.dept_partition partition(month='201708');
hive (default)> load data local inpath '/opt/module/datas/dept.txt' into table default.dept_partition partition(month='201707');


3. 查询表数据

select * from dept_partition where month='201709';


4. 联合查询表数据

select * from dept_partition where month='201709' union
select * from dept_partition where month='201708' union
select * from dept_partition where month='201707';


5.17 新增分区

hive (default)> alter table dept_partition add partition(month='201705') partition(month='201704');


5.18 删除分区

hive (default)> alter table dept_partition drop partition(month='201705'), partition(month='201704');


5.19 查询分区表有多少分区

show partitions dept_partition;


5.20 查询分区表的结构

desc formatted dept_partition;


5.21 创建二级分区表

  1. 新建表
create table dept_partition2(deptno int, dname string, loc string)
partitioned by (month string, day string)
row format delimited fields terminated by '\\t';


  1. 加载数据
load data local inpath '/opt/module/datas/dept.txt' into table
 default.dept_partition2 partition(month='201709', day='13');


5.22 修复分区

msck repair table dept_partition2;


5.23 重命名表

alter table dept_partition2 rename to dept_partition3;


5.24 添加列

alter table dept_partition add columns(deptdesc string);


5.25 更新列

alter table dept_partition change column deptdesc desc int;


5.26 替换列

alter table dept_partition replace columns(deptno string, dname  
 string, loc string);


六、DML数据库操作语言

6.1 数据导入

6.1.1 向表中装载数据

load data [local] inpath '/opt/module/datas/student.txt' [overwrite] into table student [partition (partcol1=val1,…)];
# local:表示从本地加载数据到hive表;否则从HDFS加载数据到hive表
# overwrite:表示覆盖表中已有数据,否则表示追加

6.1.2 通过查询语句向表中插入数据

# 基本插入
insert into table student values(1,'wangwu'),(2,'zhaoliu');
# 查询插入
insert overwrite table student
select id, name from student where id>10;

6.1.3 查询语句中创建表并加载数据(As Select)

create table if not exists student3 as select id, name from student;

6.1.4 创建表时通过Location指定加载数据路径

上传数据至HDFS

hive (default)> dfs -mkdir /student;
hive (default)> dfs -put /opt/module/datas/student.txt /student;

创建表,并指定HDFS上的位置

create external table if not exists student5(id int, name string)
row format delimited fields terminated by '\\t'
location '/student;

查询数据

hive (default)> select * from student5;

6.1.5 Export导出到HDFS上

# 先用export导出后,再将数据导入
import table student2 from '/user/hive/warehouse/export/student';

6.2 数据导出

6.2.1 Insert导出

# 导出到本地
insert overwrite local directory '/opt/module/datas/export/student1'
row format delimited fields terminated by '\\t'
select * from student;
# 导出到HDFS
insert overwrite directory '/user/xuzheng/student2'
row format delimited fields terminated by '\\t'
select * from student;

6.2.2 Hadoop命令导出到本地

hive (default)> dfs -get /user/hive/warehouse/student/month=201709/000000_0
/opt/module/datas/export/student3.txt;

6.2.3 Hive Shell 命令导出

bin/hive -e 'select * from default.student;' > /opt/module/datas/export/student4.txt;

6.2.4 Export导出到HDFS上

export和import主要用于两个Hadoop平台集群之间Hive表迁移

# 既能导出元数据,也能导出数据
export table default.student to '/user/hive/warehouse/export/student';

6.3 清除数据(不清除元数据)

# Truncate只能删除管理表,不能删除外部表中数据
truncate table student;

七、基本查询

(1)写SQL语句关键字的顺序

select
from
join on
where
group by
order by
having
limit

(1)执行SQL语句关键字的顺序

from
join
on
where
group by(开始使用select中的别名,后面的语句中都可以使用)
avg,sum....
having
select
distinct
order by
limit

(1)谓词下推

先走过滤,再走查询
(1)SQL优化

在join on条件中,可以使用子查询语句仅需优化

select
id,name
from
A join B
on A.id=B.id and A.id>100;
优化后================================
select
id,name
from
(select id,name from A where A.id>100) t1
join B
on t1.id=B.id;

(2) 数据准备

# 创建部门表
create table if not exists dept(deptno int, dname string, loc int)
row format delimited fields terminated by '\\t';

# 创建员工表
create table if not exists emp(empno int, ename string, job string, mgr int, hiredate string, sal double, comm double, deptno int)
row format delimited fields terminated by '\\t';

# 向部门表导入数据
load data local inpath '/home/xuzheng/dept.txt' into table dept;

# 向员工表导入数据
load data local inpath '/home/xuzheng/emp.txt' into table emp;

(3) 全表和特定列查询

# 全表查询
select * from emp;
# 特定列查询
select empno, ename from emp;

(4) 列别名

select ename as name, deptno dn from emp;

(5)算术运算符
运算符 描述
A + B A和B相加
A - B A和B相减
A * B A和B相乘
A / B A和B相除
A % B A对B取余
A & B A和B按位取余
A|B A和B按位取或
A ^ B A和B按位取异或
~A A按位取反

select sal +1 from emp;

(6)常用函数

# 求总行数
select count(*) cnt from emp;
# 求最大值
select max(sal) max_sal from emp;
# 求最小值
select min(sal) min_sal from emp;
# 求总和
select sum(sal) sum_sal from emp;
# 求平均值
select avg(sal) avg_sal from emp;

(7) Limit语句

select * from emp limit 5;

(8)where语句

# where子句中不能使用字段别名
select * from emp where sal >1000;

(9)比较运算符
操作符 支持的数据类型 描述
A = B 基本数据类型 如果A等于B则返回TRUE,反之返回FALSE
A <=> B 基本数据类型 如果A和B都为NULL,则返回TRUE,其他的和等号(=)操作符的结果一致,如果任一为NULL则结果为NULL
A<>B, A!=B 基本数据类型 A或者B为NULL则返回NULL;如果A不等于B,则返回TRUE,反之返回FALSE
A<B 基本数据类型 A或者B为NULL,则返回NULL;如果A小于B,则返回TRUE,反之返回FALSE
A<=B 基本数据类型 A或者B为NULL,则返回NULL;如果A小于等于B,则返回TRUE,反之返回FALSE
A>B 基本数据类型 A或者B为NULL,则返回NULL;如果A大于B,则返回TRUE,反之返回FALSE
A>=B 基本数据类型 A或者B为NULL,则返回NULL;如果A大于等于B,则返回TRUE,反之返回FALSE
A [NOT] BETWEEN B AND C 基本数据类型 如果A,B或者C任一为NULL,则结果为NULL。如果A的值大于等于B而且小于或等于C,则结果为TRUE,反之为FALSE。如果使用NOT关键字则可达到相反的效果。
A IS NULL 所有数据类型 如果A等于NULL,则返回TRUE,反之返回FALSE
A IS NOT NULL 所有数据类型 如果A不等于NULL,则返回TRUE,反之返回FALSE
IN(数值1, 数值2) 所有数据类型 使用 IN运算显示列表中的值
A [NOT] LIKE B STRING 类型 B是一个SQL下的简单正则表达式,也叫通配符模式,如果A与其匹配的话,则返回TRUE;反之返回FALSE。B的表达式说明如下:‘x%’表示A必须以字母‘x’开头,‘%x’表示A必须以字母’x’结尾,而‘%x%’表示A包含有字母’x’,可以位于开头,结尾或者字符串中间。如果使用NOT关键字则可达到相反的效果。
A RLIKE B, A REGEXP B STRING 类型 B是基于java的正则表达式,如果A与其匹配,则返回TRUE;反之返回FALSE。匹配使用的是JDK中的正则表达式接口实现的,因为正则也依据其中的规则。例如,正则表达式必须和整个字符串A相匹配,而不是只需与其字符串匹配。

# 查询出薪水等于5000的所有员工
select * from emp where sal = 5000;
# 查询工资在500到1000的员工信息
select * from emp where sal between 500 and 1000;
# 查询comm为空的所有员工信息
select * from emp where comm is null;
# 查询工资是1500或5000的员工信息
select * from emp where sal IN (1500, 5000);

(10)like和rlike

**rlike:**带有正则表达式的like语句
正则匹配 描述
\\ 转义
^ 一行的开头
^R 匹配以R为开头的行
$ 匹配一行的结尾
R$ 匹配以R为结尾的行

  • 表示上一个子式匹配0次或多次,贪心匹配
    Zo* Zo Zoo Zooo
    . 匹配一个任意的字符
    .* 匹配任意字符串
    [] 匹配某个范围内的字符
    [a-z] 匹配一个a-z之间的字符
    [a-z]* 匹配任意字母字符串
# 查找以2开头薪水的员工信息
select * from emp where sal LIKE '2%';
# 查找第二个数值为2的薪水的员工信息
select * from emp where sal LIKE '_2%';
# 查找薪水中含有2的员工信息
select * from emp where sal RLIKE '[2]';

(11)逻辑运算符
操作符 描述
and 逻辑并
or 逻辑或
not 逻辑否

# 查询薪水大于1000,部门是30
select * from emp where sal>1000 and deptno=30;
# 查询薪水大于1000,或者部门是30
select * from emp where sal>1000 or deptno=30;
# 查询除了20部门和30部门以外的员工信息
select * from emp where deptno not IN(30, 20);

八、分组

8.1 group by语句

# 计算emp表每个部门的平均工资
select t.deptno, avg(t.sal) avg_sal from emp t group by t.deptno;
# 计算emp每个部门中每个岗位的最高薪水
select t.deptno, t.job, max(t.sal) max_sal from emp t group by
t.deptno, t.job;

8.2 having语句

having和where不同点

where后面不能写分组函数,而having后面可以使用分组函数  
having只用于group by分组统计语句


# 求每个部门的平均工资
select deptno, avg(sal) from emp group by deptno;
# 求每个部门的平均薪水大于2000的部门
select deptno, avg(sal) avg_sal from emp group by deptno having
avg_sal > 2000;

九、join语句

9.1 等值join

# 根据员工表和部门表中的部门编号相等,查询员工编号、员工名称和部门名称;
select e.empno, e.ename, d.deptno, d.dname from emp e join dept d on e.deptno = d.deptno;

9.2 表的别名

# 合并员工表和部门表
select e.empno, e.ename, d.deptno from emp e join dept d on e.deptno = d.deptno;

9.3 内连接

# 只有进行连接的两个表中都存在与连接条件相匹配的数据才会被保留下来
select e.empno, e.ename, d.deptno from emp e join dept d on e.deptno = d.deptno;

9.4 左外连接

# JOIN操作符左边表中符合WHERE子句的所有记录将会被返回
select e.empno, e.ename, d.deptno from emp e left join dept d on e.deptno = d.deptno;

9.5 右外连接

# JOIN操作符右边表中符合WHERE子句的所有记录将会被返回
select e.empno, e.ename, d.deptno from emp e right join dept d on e.deptno = d.deptno;

9.6 满外连接

# 将会返回所有表中符合WHERE语句条件的所有记录。如果任一表的指定字段没有符合条件的值的话,那么就使用NULL值替代
select e.empno, e.ename, d.deptno from emp e full join dept d on e.deptno = d.deptno;

9.7 多表连接

**创建位置表 **

create table if not exists location(loc int, loc_name string)
row format delimited fields terminated by '\\t';

导入数据

load data local inpath '/home/xuzheng/location.txt' into table location;

多表连接查询

SELECT e.ename, d.dname, l.loc_name
FROM emp e
JOIN dept d
ON d.deptno = e.deptno
JOIN location l
ON d.loc = l.loc;

9.8 笛卡尔积

hive中严禁使用笛卡尔积

产生笛卡尔的条件

省略连接条件  
连接条件无效  
所有表中的所有行互相连接


9.9 连接谓词中不支持or

hive join目前不支持在on子句中使用谓词or

十、排序

10.1 全局排序

排序规则

asc: 升序  
desc:降序

# 查询员工信息按工资升序排列
select * from emp order by sal;
# 查询员工信息按工资降序排列
select * from emp order by sal desc;

10.2 按照别名排序

# 按照员工薪水的2倍排序
select ename, sal*2 twosal from emp order by twosal;

10.3 多个列排序

# 按照部门和工资升序排序
select ename, deptno, sal from emp order by deptno, sal;

10.4 每个MapReduce内部排序

Sort By:对于大规模的数据集order by的效率非常低。在很多情况下,并不需要全局排序,此时可以使用sort by,按照分区排序。

Sort by为每个reducer产生一个排序文件。每个Reducer内部进行排序,对全局结果集来说不是排序。

# 设置reduce个数
set mapreduce.job.reduces=3;
# 查看设置reduce个数
set mapreduce.job.reduces;
# 根据部门编号降序查看员工信息
select * from emp sort by deptno desc;
# 将查询结果导入到文件中(按照部门编号降序排序)
insert overwrite local directory '/home/xuzheng/datas/sortby-result' select * from emp sort by deptno desc;

10.5 分区排序

Distribute By: 在有些情况下,我们需要控制某个特定行应该到哪个reducer,通常是为了进行后续的聚集操作。distribute by 子句可以做这件事。distribute by类似MR中partition(自定义分区),进行分区,结合sort by使用。

对于distribute by进行测试,一定要分配多reduce进行处理,否则无法看到distribute by的效果。

# 设置reduce个数
set mapreduce.job.reduces=3;
# 先按照部门编号分区,再按照员工编号降序排序
select * from emp distribute by deptno sort by empno desc;

排序规则

distribute by的分区规则是根据分区字段的hash码与reduce的个数进行模除后,余数相同的分到一个区  
Hive要求DISTRIBUTE BY语句要写在SORT BY语句之前

10.6 Cluster By

当distribute by和sorts by字段相同时,可以使用cluster by方式

cluster by除了具有distribute by的功能外还兼具sort by的功能。但是排序只能是升序排序,不能指定排序规则为ASC或者DESC

# 按照部门编号分区排序
select * from emp cluster by deptno;
# 与上面语句等价
select * from emp distribute by deptno sort by deptno;

十一、分桶及抽样查询

11.1 分桶表数据存储

创建分桶表

create table stu_buck(id int, name string)
clustered by(id)
into 4 buckets
row format delimited fields terminated by '\\t';

创建临时表

create table stu(id int, name string)
row format delimited fields terminated by '\\t';

导入数据至临时表

load data local inpath '/home/xuzheng/student.txt' into table stu;

设置强制分桶

set hive.enforce.bucketing=true;

设置reduce个数

# 让hive自己去决定分桶个数
set mapreduce.job.reduces=-1;

导入数据至分桶表

insert into stu_buck select * from stu;

11.2 分桶抽样查询

tablesample((bucket x out of y)

y必须是table总bucket数的倍数或者因子。hive根据y的大小,决定抽样的比例。例如,table总共分了4份,当y=2时,抽取(4/2=)2个bucket的数据,当y=8时,抽取(4/8=)1/2个bucket的数据

# 按照id抽样查询,将数据分4份,每一份取第1个数据
select * from stu_buck tablesample(bucket 1 out of 4 on id);

十二、其他常用查询函数

12.1 空字段赋值

函数说明  
NVL:给值为NULL的数据赋值,它的格式是NVL( value,default\\_value)。它的功能是如果value为NULL,则NVL函数返回default\\_value的值,否则返回value的值,如果两个参数都为NULL ,则返回NULL。

# 如果员工的comm为NULL,则用-1代替
select comm,nvl(comm, -1) from emp;

# 如果员工的comm为NULL,则用领导id代替
select comm, nvl(comm,mgr) from emp;

12.2 时间类

date_format格式化时间

select date_format('2019-06-12', 'yyyy-MM-dd');

date_add时间相加天数

select date_add('2019-06-12', 5);
select date_add('2019-06-12', -5);

date_sub时间相减天数

select date_sub('2019-06-12', 5);
select date_sub('2019-06-12', -5);

两个时间相减得天数

select datediff('2019-06-12', '2019-06-10');

替换函数

select regexp_replace('2019/06/12', '/', '-');

12.3 CASE WHEN

数据准备

姓名 部门 性别
悟空 A 男
大海 A 男
宋宋 B 男
凤姐 A 女
婷姐 B 女
婷婷 B 女

创建表

create table emp_sex(name string, dept_id string, sex string)
row format delimited fields terminated by "\\t";

导入数据

load data local inpath '/home/xuzheng/emp_sex.txt' into table emp_sex;

查询语句

# 求出不同部门男女各多少人。结果如下:
select
dept_id,
sum(case sex when '男' then 1 else 0 end) male_count,
sum(case sex when '女' then 1 else 0 end) female_count
from
emp_sex
group by
dept_id;

12.4 行转列

数据准备

姓名 星座 血型
孙悟空 白羊座 A
大海 射手座 A
宋宋 白羊座 B
猪八戒 白羊座 A
凤姐 射手座 A

**需求  **
把星座和血型一样的人归类到一起。结果如下:

射手座,A 大海|凤姐
白羊座,A 孙悟空|猪八戒
白羊座,B 宋宋|苍老师

创建本地constellation.txt

vi constellation.txt
孙悟空 白羊座 A
大海 射手座 A
宋宋 白羊座 B
猪八戒 白羊座 A
凤姐 射手座 A

创建hive表

create table person_info(name string, constellation string, blood_type string)
row format delimited fields terminated by "\\t";

导入数据

load data local inpath "/home/xuzheng/constellation.txt" into table person_info;

查询语句

select
t1.base,
concat_ws('|', collect_set(t1.name)) name
from
(select
name,
concat(constellation, ",", blood_type) base
from
person_info) t1
group by
t1.base;

12.5 列转行

数据准备

电源 分类
《疑犯追踪》 悬疑,动作,科幻,剧情
《Lie to me》 悬疑,警匪,动作,心理,剧情
《战狼2》 战争,动作,灾难

**需求  **
将电影分类中的数组数据展开。结果如下:

《疑犯追踪》 悬疑
《疑犯追踪》 动作
《疑犯追踪》 科幻
《疑犯追踪》 剧情
《Lie to me》 悬疑
《Lie to me》 警匪
《Lie to me》 动作
《Lie to me》 心理
《Lie to me》 剧情
《战狼2》 战争
《战狼2》 动作
《战狼2》 灾难

创建本地movie.txt

vi movie.txt
《疑犯追踪》 悬疑,动作,科幻,剧情
《Lie to me》 悬疑,警匪,动作,心理,剧情
《战狼2》 战争,动作,灾难

创建hive表

create table movie_info(movie string, category array<string>)
row format delimited fields terminated by "\\t"
collection items terminated by ",";

导入数据

load data local inpath "/home/xuzheng/movie.txt" into table movie_info;

查询语句

select
movie,
category_name
from
movie_info lateral view explode(category) table_tmp as category_name;

12.6 窗口函数

数据准备

姓名 购买日期 价格
jack 2017-01-01 10
tony 2017-01-02 15
jack 2017-02-03 23
tony 2017-01-04 29
jack 2017-01-05 46
jack 2017-04-06 42
tony 2017-01-07 50
jack 2017-01-08 55
mart 2017-04-08 62
mart 2017-04-09 68
neil 2017-05-10 12
mart 2017-04-11 75
neil 2017-06-12 80
mart 2017-04-13 94

创建本地business.txt

vi business.txt

创建hive表

create table business(name string, orderdate string,cost int)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';

导入数据

load data local inpath "/home/xuzheng/business.txt" into table business;

按需求查询数据

查询在2017年4月份购买过的顾客及总人数

select name,count(*) over ()
from business
where substring(orderdate,1,7) = '2017-04'
group by name;

查询顾客的购买明细及月购买总额

select name,orderdate,cost,sum(cost) over(partition by name, month(orderdate)) from business;

上述的场景, 将每个顾客的cost按照日期进行累加

select name,orderdate,cost,
sum(cost) over() as sample1,--所有行相加
sum(cost) over(partition by name) as sample2,--按name分组,组内数据相加
sum(cost) over(partition by name order by orderdate) as sample3,--按name分组,组内数据累加
sum(cost) over(partition by name order by orderdate rows between UNBOUNDED PRECEDING and current row ) as sample4 ,--和sample3一样,由起点到当前行的聚合
sum(cost) over(partition by name order by orderdate rows between 1 PRECEDING and current row) as sample5, --当前行和前面一行做聚合
sum(cost) over(partition by name order by orderdate rows between 1 PRECEDING AND 1 FOLLOWING ) as sample6,--当前行和前边一行及后面一行
sum(cost) over(partition by name order by orderdate rows between current row and UNBOUNDED FOLLOWING ) as sample7 --当前行及后面所有行
from business;

查询每个顾客上次的购买时间

select name,orderdate,cost,
lag(orderdate,1,'1900-01-01') over(partition by name order by orderdate ) as time1, lag(orderdate,2) over (partition by name order by orderdate) as time2
from business;

查询前20%时间的订单信息

select * from (
select name,orderdate,cost, ntile(5) over(order by orderdate) sorted
from business
) t
where sorted = 1;

over函数中的2种组合

select
name,
orderdate,
cost,
sum(cost) over(distribute by name sort by orderdate)
from
business;

select
name,
orderdate,
cost,
sum(cost) over(partition by name order by orderdate)
from
business;

12.7 Rank

函数说明

RANK() 排序相同时会重复,总数不会变 1 1 3 4  
DENSE_RANK() 排序相同时会重复,总数会减少 1 1 2 3  
ROW_NUMBER() 会根据顺序计算 1 2 3 4


数据准备

姓名 科目 成绩
孙悟空 语文 87
孙悟空 数学 95
孙悟空 英语 68
大海 语文 94
大海 数学 56
大海 英语 84
宋宋 语文 64
宋宋 数学 86
宋宋 英语 84
婷婷 语文 65
婷婷 数学 85
婷婷 英语 78

需求  
计算每门学科成绩排名  
创建本地score.txt

vi score.txt

创建hive表

create table score(
name string,
subject string,
score int)
row format delimited fields terminated by "\\t";

导入数据

load data local inpath '/home/xuzheng/score.txt' into table score;

按需求查询数据

select name,
subject,
score,
rank() over(partition by subject order by score desc) rp,
dense_rank() over(partition by subject order by score desc) drp,
row_number() over(partition by subject order by score desc) rmp
from score;

12.8 系统函数

查看系统提供的函数

show functions;

显示指定函数的用法

desc function upper;

详细介绍指定函数的具体用法

desc function extended split;

十三、自定义函数UDF

13.1 自定义函数类型

  • UDF函数(user-defined function)

一进一出的函数

  • UDAF函数(user-defined aggregation function)

多进一出的函数,例如count、max、min

  • UDF函数(user-defined table-generating functions)

一进多出的函数,例如later view explore()炸裂函数

13.2 创建项目导入依赖

<dependencies>
	<dependency>
		<groupId>org.apache.hive</groupId>
		<artifactId>hive-exec</artifactId>
		<version>1.2.1</version>
	</dependency>
</dependencies>

13.3 创建一个类继承与UDF

package com.inspur.hive;

import org.apache.hadoop.hive.ql.exec.UDF;

public class Lower extends UDF 
	public int evaluate(String line) 
		if (line == null) 
		return 0;
		 else 
		return line.length();
		
	
	public int evalute(Number line)   
		if (line == null)   
			return 0;  
		 else   
			return line.toString().length();  
		  
	

	public int evalute(Boolean line)   
		if (line == null)   
			return 0;  
		 else   
			return line.toString().length();  
		  
	  


13.4 打成jar包,并上传集群

13.5 临时上传jar包至hive,退出时失效

add jar /home/lytdev/1.jar;

13.6 创建自定义函数

create function mylen as "com.inspur.hive.Lower";

13.7 测试自定义函数

select ename, mylen(ename) from emp;

十四、压缩与存储

14.1 开启Map输出阶段压缩

开启hive中间传输数据压缩功能

hive (default)>set hive.exec.compress.intermediate=true;

开启mapreduce中map输出压缩功能

hive (default)>set mapreduce.map.output.compress=true;

设置mapreduce中map输出数据的压缩方式

hive (default)>set mapreduce.map.output.compress.codec = org.apache.hadoop.io.compress.SnappyCodec;

执行查询语句

hive (default)> select count(ename) name from emp;

14.2 开启Reduce输出阶段压缩

开启hive最终输出数据压缩功能

hive (default)>set hive.exec.compress.output=true;

开启mapreduce最终输出数据压缩

hive (default)>set mapreduce.output.fileoutputformat.compress=true;

设置mapreduce最终数据输出压缩方式

hive (default)> set mapreduce.output.fileoutputformat.compress.codec =
org.apache.hadoop.io.compress.SnappyCodec;

设置mapreduce最终数据输出压缩为块压缩

hive (default)> set mapreduce.output.fileoutputformat.compress.type=BLOCK;

测试一下输出结果是否是压缩文件

hive (default)> insert overwrite local directory
'/home/xuzheng/distribute-result' select * from emp distribute by deptno sort by empno desc;

14.3 文件存储格式

Hive支持的存储数据的格式主要有:TEXTFILE 、SEQUENCEFILE、ORC、PARQUET

14.4 列式存储和行式存储

  • 行存储的特点

查询满足条件的一整行数据的时候,列存储则需要去每个聚集的字段找到对应的每个列的值,行存储只需要找到其中一个值,其余的值都在相邻地方,所以此时行存储查询的速度更快。

  • 列存储的特点

因为每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量;每个字段的数据类型一定是相同的,列式存储可以针对性的设计更好的设计压缩算法。

  • TEXTFILE和SEQUENCEFILE的存储格式都是基于行存储的;
  • ORC和PARQUET是基于列式存储的;
  • ORC常用于MapReduce,PARQUET常用于spark。

14.5 存储和压缩结合

创建一个非压缩的的ORC存储方式

创建一个orc表

create table log_orc_none(
track_time string,
url string,
session_id string,
referer string,
ip string,
end_user_id string,
city_id string
)
row format delimited fields terminated by '\\t'
stored as orc tblproperties ("orc.compress"="NONE");

插入数据

insert into table log_orc select * from log_text;

查看表中数据大小

dfs -du -h /user/hive/warehouse/log_orc/;

创建一个SNAPPY压缩的ORC存储方式

创建一个orc表

create table log_orc_snappy(
track_time string,
url string,
session_id string,
referer string,
ip string,
end_user_id string,
city_id string
)
row format delimited fields terminated by '\\t'
stored as orc tblproperties ("orc.compress"="SNAPPY");

插入数据

insert into table log_orc_snappy select * from log_text;

查看表中数据大小

dfs -du -h /user/hive/warehouse/log_orc_snappy/;

**存储方式和压缩总结  **

在实际的项目开发当中,hive表的数据存储格式一般选择:orc或parquet。压缩方式一般选择snappy,lzo

十五、企业级调优

15.1 Fetch抓取

  • Fetch抓取

Hive中对某些情况的查询可以不必使用MapReduce计算。例如:SELECT * FROM employees;在这种情况下,Hive可以简单地读取employee对应的存储目录下的文件,然后输出查询结果到控制台。

  • Fetch参数配置
<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>

案例实操1
把hive.fetch.task.conversion设置成none,然后执行查询语句,都会执行mapreduce程序

set hive.fetch.task.conversion=none;
select * from emp;
select ename from emp;
select ename from emp limit 3;

案例实操2
把hive.fetch.task.conversion设置成more,然后执行查询语句,如下查询方式都不会执行mapreduce程序

set hive.fetch.task.conversion=more;
select * from emp;
select ename from emp;
select ename from emp limit 3;

15.2 本地模式

意义
Hive可以通过本地模式在单台机器上处理所有的任务。对于小数据集,执行时间可以明显被缩短
开启本地模式

# 开启本地mr
set hive.exec.mode.local.auto=true;
# 设置local mr的最大输入数据量,当输入数据量小于这个值时采用local mr的方式,默认为134217728,即128M
set hive.exec.mode.local.auto.inputbytes.max=50000000;
# 设置local mr的最大输入文件个数,当输入文件个数小于这个值时采用local mr的方式,默认为4
set hive.exec.mode.local.auto.input.files.max=10;

15.3 小表、大表join

新版的hive已经对小表JOIN大表和大表JOIN小表进行了优化。小表放在左边和右边已经没有明显区别。

15.3.1 需求

测试大表join小表和小表join大表的效率

15.3.2 建大表、小表和join大表的语句

(1)创建大表

create table bigtable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\\t';

(2)创建小表

create table smalltable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\\t';

(3)创建join后的表

create table jointable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\\t';

(4)导入数据

load data local inpath '/home/xuzheng/bigtable' into table bigtable;
load data local inpath '/home/xuzheng/smalltable' into table smalltable;

(5)关闭mapjoin功能(默认是打开的)

set hive.auto.convert.join = false;

(6)执行小表JOIN大表语句

insert overwrite table jointable
select b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url
from smalltable s
join bigtable b
on b.id = s.id;

(7)执行结果

MapReduce Total cumulative CPU time: 31 seconds 100 msec
No rows affected (52.897 seconds)

(8)执行大表JOIN小表语句

insert overwrite table jointable
select b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url
from bigtable b
join smalltable s
on s.id = b.id;

(9)执行结果

MapReduce Total cumulative CPU time: 29 seconds 790 msec
No rows affected (50.443 seconds)

(10)注意
大表放在左边 left join 小表,可以走mapjoin进行优化;
如果使用 join,也就是inner join 大表小表的左右顺序无所谓,都会进行优化

15.4 大表和大表join

15.4.1 空key过滤

进行空值过滤时,放在子查询中

insert overwrite table jointable select n.* from (select * from nullidtable where id is not null ) n left join ori o on n.id = o.id;

15.4.2 空key替换

有时虽然某个key为空对应的数据很多,但是相应的数据不是异常数据,必须要包含在join的结果中,此时我们可以表a中key为空的字段赋一个随机的值,使得数据随机均匀地分不到不同的reducer上,避免数据倾斜,数据倾斜会导致MapTask或者ReduceTask执行不了,从而导致整个MR任务执行不了。

insert overwrite table jointable
select n.* from nullidtable n full join ori o on
case when n.id is null then concat('hive', rand()) else n.id end = o.id;

15.5 开启mapjoin参数配置

  • 设置自动选择Mapjoin
set hive.auto.convert.join = true; #默认为true

  • 大表小表的阈值设置(默认25M一下认为是小表)
set hive.mapjoin.smalltable.filesize=25000000

当服务器内容256GB时,可以增大该参数配置

15.6 group by

默认情况下,Map阶段同一Key数据分发给一个reduce,当一个key数据过大时就倾斜了。并不是所有的聚合操作都需要在Reduce端完成,很多聚合操作都可以先在Map端进行部分聚合,最后在Reduce端得出最终结果。

  • 是否在Map端进行聚合,默认为True
set hive.map.aggr = true

  • 在Map端进行聚合操作的条目数目
set hive.

以上是关于深入浅出学习Hive的主要内容,如果未能解决你的问题,请参考以下文章

深入浅出学习Hive

深入学习Hive应用场景及架构原理

数盟培训HIVE+MAHOUT深入浅出

深入学习Hive应用场景及架构原理

Hive 内建操作符与函数开发——深入浅出学Hive

深入理解 Hive UDAF