1 Hive 基本概念
1.1 什么是 Hive?
Hive:由 Facebook 开源用于解决海量结构化日志的数据统计。
Hive 是基于 Hadoop 的一个数据仓库工具,可以将结构化的数据文件映射为一张表,并提供类 SQL 查询功能。
- Hive 处理的数据存储在 HDFS
- Hive 分析数据底层的实现是 MapReduce
- 执行程序运行在 Yarn 上
1.2 Hive 的优缺点
优点:
- 操作接口采用类 SQL 语法,提供快速开发的能力
- 避免了去写 MapReduce,减少开发人员的学习成本
- Hive 的执行延迟比较高,因此 Hive 常用于数据分析,对实时性要求不高的场合
- Hive 优势在于处理大数据,对于处理小数据没有优势,因为 Hive 的执行延迟比较高
- Hive 支持用户自定义函数,用户可以根据自己的需求来实现自己的函数
缺点:
- Hive 的 HQL 表达能力有限
- 迭代式算法无法表达
- 数据挖掘方面不擅长,由于 MapReduce 数据处理流程的限制,效率更高的算法却无法实现
- 迭代式算法无法表达
- Hive的效率比较低
- Hive 自动生成的 MapReduce 作业,通常情况下不够智能化
- Hive 调优比较困难,粒度较粗
1.3 Hive架构原理
1、用户接口:Client
- CLI
- JDBC
2、元数据:Metastore
- 包括表名、表所属的数据库(默认是 default)、表的拥有者、列/分区字段、表的类型(是否是外部表)、表的数据所在目录等
- 默认存储在自带的 derby 数据库中,推荐使用 MySQL 存储 Metastore
3、Hadoop
- 使用 HDFS 进行存储,使用 MapReduce 进行计算。
4、驱动器
- 解析器(SQL Parser):将 SQL 字符串转换成抽象语法树 AST,对 AST 进行语法分析,比如表是否存在、字段是否存在、SQL语义是否有误
- 编译器(Physical Plan):将 AST 编译生成逻辑执行计划
- 优化器(Query Optimizer):对逻辑执行计划进行优化
- 执行器(Execution):把逻辑执行计划转换成可以运行的物理计划,对于 Hive 来说,就是 MR/Spark
Hive 通过给用户提供的一系列交互接口,接收到用户的指令(SQL),使用自己的 Driver,结合元数据(MetaStore),将这些指令翻译成 MapReduce,提交到 Hadoop 中执行,最后,将执行返回的结果输出到用户交互接口。
1.4 Hive VS 数据库
- 由于 SQL 被广泛的应用在数据库中,因此,专门针对 Hive 的特性设计了类 SQL 的查询语言 HQL
- Hive 中的数据存储到 HDFS 中,而数据库则可以将数据保存在块设备或者本地文件系统中
- Hive 中不建议对数据的改写,所有的数据都是在加载的时候确定好的,数据库支持更新操作
- Hive 的查询执行由 MapReduce 来实现,数据库有自己的执行引擎
- Hive 在查询数据的时候,由于没有索引,需要扫描整个表,因此延迟较高,另外一个导致 Hive 执行延迟高的因素是 MapReduce 框架,由于MapReduce 本身具有较高的延迟,因此在利用 MapReduce 执行 Hive 查询时,也会有较高的延迟,相对的,数据库的执行延迟较低,当然,这个低是有条件的,即数据规模较小,当数据规模大到超过数据库的处理能力的时候,Hive 的并行计算显然能体现出优势
- Hive 的扩展性高,数据库的可扩展性较低
- Hive 建立在集群上并可以利用 MapReduce 进行并行计算,因此可以支持很大规模的数据,对应的,数据库可以支持的数据规模较小
2 Hive 安装
2.1 Hive 安装部署
1、解压 apache-hive-1.2.1-bin.tar.gz 到 /opt/module/ 目录下面
2、修改 apache-hive-1.2.1-bin.tar.gz 的名称为 hive
3、修改 /opt/module/hive/conf 目录下的 hive-env.sh.template 名称为 hive-env.sh
4、配置 hive-env.sh
# 配置HADOOP_HOME路径
export HADOOP_HOME=/opt/module/hadoop-2.7.2
# 配置HIVE_CONF_DIR路径
export HIVE_CONF_DIR=/opt/module/hive/conf
5、启动 hdfs、yarn
6、在 HDFS 上创建 /tmp 和 /user/hive/warehouse 两个目录并修改他们的同组权限可写
[djm@hadoop102 hadoop-2.7.2]$ bin/hadoop fs -mkdir /tmp
[djm@hadoop102 hadoop-2.7.2]$ bin/hadoop fs -mkdir -p /user/hive/warehouse
[djm@hadoop102 hadoop-2.7.2]$ bin/hadoop fs -chmod g+w /tmp
[djm@hadoop102 hadoop-2.7.2]$ bin/hadoop fs -chmod g+w /user/hive/warehouse
7、启动 Hive
[djm@hadoop102 hive]$ bin/hive
2.2 将本地文件导入到 Hive
1、在 /opt/module/ 目录下创建 datas
2、在 datas 目录下创建 student.txt 并添加如下内容
1001 zhangshan
1002 lishi
1003 zhaoliu
3、创建 student 表, 并声明列分隔符
hive> create table student(id int, name string) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t';
4、加载 /opt/module/datas/student.txt 文件到 student 数据表中
hive> load data local inpath '/opt/module/datas/student.txt' into table student;
5、再打开一个客户端窗口启动 hive,会产生java.sql.SQLException 异常:
- Metastore 默认存储在自带的 derby 数据库中,推荐使用 MySQL 存储 Metastore
6、其他字段解释
- row format delimited fields terminated by ',' --列分隔符
- collection items terminated by '_' --MAP STRUCT 和 ARRAY 的分隔符(数据分割符号)
- map keys terminated by ':' --MAP 中的 key 与 value 的分隔符
- lines terminated by '\n'; --行分隔符
2.3 安装 MariaDB
1、查看是否现有的包
rpm -qa | grep mariadb
2、如果有,删之
rpm -e --nodeps 包名
3、安装 MariaDB
yum -y install mariadb mariadb-server
4、启动 MariaDB
systemctl start mariadb
5、进行 MariaDB 的相关简单配置
mysql_secure_installation
首先是设置密码,会提示先输入密码
Enter current password for root (enter for none):<–初次运行直接回车
设置密码
Set root password? [Y/n] <– 是否设置root用户密码,输入y并回车或直接回车
New password: <– 设置root用户的密码
Re-enter new password: <– 再输入一次你设置的密码
其他配置
Remove anonymous users? [Y/n] <– 是否删除匿名用户,回车
Disallow root login remotely? [Y/n] <–是否禁止root远程登录,回车
Remove test database and access to it? [Y/n] <– 是否删除test数据库,回车
Reload privilege tables now? [Y/n] <– 是否重新加载权限表,回车
2.4 Hive 元数据配置到 MySQL
1、驱动拷贝:拷贝 /opt/software 目录下的 mysql-connector-java-5.1.27-bin.jar 到 /opt/module/hive/lib/
2、配置 Metastore 到 MySQL:在 /opt/module/hive/conf 目录下创建 hive-site.xml 并编辑
javax.jdo.option.ConnectionURL
jdbc:mysql://hadoop102:3306/metastore?createDatabaseIfNotExist=true
JDBC connect string for a JDBC metastore
javax.jdo.option.ConnectionDriverName
com.mysql.jdbc.Driver
Driver class name for a JDBC metastore
javax.jdo.option.ConnectionUserName
root
username to use against metastore database
javax.jdo.option.ConnectionPassword
000000
password to use against metastore database
2.4 HiveJDBC 访问
1、启动 hiveserver2 服务
[djm@hadoop102 hive]$ bin/hiveserver2
2、启动 beeline
[djm@hadoop102 hive]$ bin/beeline
Beeline version 1.2.1 by Apache Hive
beeline>
3、连接 hiveserver2
beeline> !connect jdbc:hive2://hadoop102:10000(回车)
Connecting to jdbc:hive2://hadoop102:10000
Enter username for jdbc:hive2://hadoop102:10000: djm(回车)
Enter password for jdbc:hive2://hadoop102:10000: (直接回车)
Connected to: Apache Hive (version 1.2.1)
Driver: Hive JDBC (version 1.2.1)
Transaction isolation: TRANSACTION_REPEATABLE_READ
0: jdbc:hive2://hadoop102:10000>
2.5 Hive 常见属性配置
2.5.1 Hive 数据仓库位置配置
Default 数据仓库的最原始位置是在 hdfs 上的:/user/hive/warehouse 路径下
在仓库目录下,没有对默认的数据库 default 创建文件夹,如果某张表属于 default 数据库,直接在数据仓库目录下创建一个文件夹
修改 default 数据仓库原始位置,在 hive-site.xml 配置,添加如下配置:
hive.metastore.warehouse.dir
/user/hive/warehouse
location of default database for the warehouse
配置同组用户有执行权限
bin/hdfs dfs -chmod g+w /user/hive/warehouse
2.5.2 查询后信息显示配置
在 hive-site.xml 文件中添加如下配置信息,就可以实现显示当前数据库,以及查询表的头信息配置
hive.cli.print.header
true
hive.cli.print.current.db
true
2.5.3 Hive 运行日志信息配置
Hive 的 log 默认存放在 /tmp/djm/hive.log 目录下
修改 hive 的 log 存放日志到 /opt/module/hive/logs
修改 conf/hive-log4j.properties.template 文件名称为 hive-log4j.properties
-
在 hive-log4j.properties 文件中修改 log 存放位置
hive.log.dir=/opt/module/hive/logs
2.5.4 参数配置方式
查看当前所有的配置信息
hive>set;
参数的配置三种方式
1、配置文件方式
默认配置文件:hive-default.xml
用户自定义配置文件:hive-site.xml
注意:用户自定义配置会覆盖默认配置,另外,Hive 也会读入 Hadoop 的配置,因为 Hive 是作为 Hadoop 的客户端启动的,Hive 的配置会覆盖 Hadoop 的配置,配置文件的设定对本机启动的所有 Hive 进程都有效
2、命令行参数方式
启动 Hive 时,可以在命令行添加 -hiveconf param=value 来设定参数
例如:
[djm@hadoop103 hive]$ bin/hive -hiveconf mapred.reduce.tasks=10;
注意:仅对本次 hive 启动有效
查看参数设置:
set mapred.reduce.tasks;
3、参数声明方式
可以在 HQL 中使用 SET 关键字设定参数
例如:
set mapred.reduce.tasks=100;
注意:仅对本次 hive 启动有效
上述三种设定方式的优先级依次递增,即配置文件<命令行参数<参数声明。注意某些系统级的参数,例如 log4j 相关的设定,必须用前两种方式设定,因为那些参数的读取在会话建立以前已经完成了
3 Hive 数据类型
3.1 基本数据类型
Hive数据类型 | Java数据类型 | 长度 | 例子 |
---|---|---|---|
TINYINT | byte | 1byte有符号整数 | 20 |
SMALINT | short | 2byte有符号整数 | 20 |
INT | int | 4byte有符号整数 | 20 |
BIGINT | long | 8byte有符号整数 | 20 |
BOOLEAN | boolean | 布尔类型,true或者false | TRUE FALSE |
FLOAT | float | 单精度浮点数 | 3.14159 |
DOUBLE | double | 双精度浮点数 | 3.14159 |
STRING | string | 字符系列,可以指定字符集,可以使用单引号或者双引号 | 'a'、"A" |
TIMESTAMP | 时间类型 | ||
BINARY | 字节数组 |
对于 Hive 的 String 类型相当于数据库的 varchar 类型,该类型是一个可变的字符串,不过它不能声明其中最多能存储多少个字符,理论上它可以存储 2GB 的字符数
3.2 集合数据类型
数据类型 | 描述 | 语法示例 |
---|---|---|
STRUCT | 和c语言中的struct类似,都可以通过“点”符号访问元素内容。例如,如果某个列的数据类型是STRUCT{first STRING, last STRING},那么第1个元素可以通过字段.first来引用。 | struct() 例如struct |
MAP | MAP是一组键-值对元组集合,使用数组表示法可以访问数据。例如,如果某个列的数据类型是MAP,其中键->值对是’first’->’John’和’last’->’Doe’,那么可以通过字段名[‘last’]获取最后一个元素 | map() 例如map |
ARRAY | 数组是一组具有相同类型和名称的变量的集合。这些变量称为数组的元素,每个数组元素都有一个编号,编号从零开始。例如,数组值为[‘John’, ‘Doe’],那么第2个元素可以通过数组名[1]进行引用。 | Array() 例如array |
3.3 类型转化
Hive 的原子数据类型是可以进行隐式转换的,类似于 Java 的类型转换,例如某表达式使用 INT 类型,TINYINT 会自动转换为 INT 类型,但是 Hive 不会进行反向转化,例如,某表达式使用 TINYINT 类型,INT 不会自动转换为 TINYINT 类型,它会返回错误,除非使用 CAST 操作
转换规则:
- 任何整数类型都可以隐式地转换为一个范围更广的范围
- 所有整数类型、FLOAT 和 STRING 类型都可以隐式地转换成 DOUBLE
- TINYINT、SMALLINT、INT 都可以转换为 FLOAT
- BOOLEAN 类型不可以转换为任何其它的类型
4 DDL
4.1 创建数据库
格式:
CREATE DATABASE [IF NOT EXISTS] database_name
[COMMENT database_comment]
[LOCATION hdfs_path]
[WITH DBPROPERTIES (property_name=property_value, ...)];
4.2 查询数据库
显示所有数据库
show databases;
过滤显示查询的数据库
show databases like 'db_hive*';
查看数据库信息
desc database db_hive;
查看数据库详情
desc database extended db_hive;
切换数据库
use db_hive;
4.3 修改数据库
格式:
ALTER DATABASE database_name
SET DBPROPERTIES (property_name=property_value, ...);
4.4 删除数据库
格式:
DROP database_name
[IF NOT EXISTS]
[CASCADE];
4.5 创建表
格式:
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]
[STORED AS file_format]
[LOCATION hdfs_path]
[TBLPROPERTIES (property_name=property_value, ...)]
[AS select_statement]
字段解释说明:
- EXTERNAL 表示该表为外部表,在建表的同时可以指定一个指向实际数据的路径,在删除表的时候,内部表的元数据和数据会被一起删除,而外部表只删除元数据,不删除数据
- PARTITIONED BY 表示该表为分区表,可以理解成 MySQL 的索引
- CLUSTERED BY 表示该表啊为分桶表
- SORTED BY不常用,对桶中的一个或多个列另外排序
- ROW FORMAT 行对象的序列与反序列化:
- 用户在建表的时候可以自定义 SerDe 或者使用自带的 SerDe,如果没有指定 ROW FORMAT 或者 ROW FORMAT DELIMITED,将会使用自带的 SerDe,在建表的时候,用户还需要为表指定列,用户在指定表的列的同时也会指定自定义的 SerDe,Hive 通过 SerDe 确定表的具体的列的数据
- STORED AS 指定存储文件类型:
- TEXTFILE 文本
- SEQUENCEFILE 二进制序列文件
- RCFILE 列式存储格式文件
- LOCATION 指定在 HDFS 上的存储位置
- AS 后跟查询语句,根据查询结果创建表
- LIKE 允许用户复制现有的表结构,但是不复制数据
4.5.1 管理表
默认创建的表都是所谓的管理表,有时也被称为内部表,因为这种表,Hive会控制着数据的生命周期,Hive 默认情况下会将这些表的数据存储在由配置项 hive.metastore.warehouse.dir 所定义的目录的子目录下,当我们删除一个管理表时,Hive 也会删除这个表中数据,管理表不适合和其他工具共享数据。
4.5.2 外部表
因为表是外部表,所以 Hive 并非认为其完全拥有这份数据。删除该表并不会删除掉这份数据,不过描述表的元数据信息会被删除掉
4.5.3 管理表与外部表的互相转换
查询表的类型
desc formatted student;
修改内部表 student 为外部表
alter table student set tblproperties('EXTERNAL'='TRUE');
查询表的类型
desc formatted student;
修改外部表 student 为内部表
alter table student set tblproperties('EXTERNAL'='FALSE');
查询表的类型
desc formatted student;
4.6 分区表
创建分区表
hive (default)> create table dept_partition(
deptno int, dname string, loc string
) partitioned by (month string) row format delimited fields terminated by '\t';
加载分区数据到分区表中
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’);
查询分区内数据
# 单分区查询
select * from dept_partition where month='201709';
# 多分区查询
select * from dept_partition where month='201709'
union select * from dept_partition where month='201708'
union select * from dept_partition where month='201707';
增加分区
# 增加单分区
alter table dept_partition add partition(month='201706');
# 增加多分区
alter table dept_partition add partition(month='201706') partition(month='201705');
删除分区
# 删除单分区
alter table dept_partition drop partition(month='201706');
# 删除多分区
alter table dept_partition drop partition(month='201706') partition(month='201705');
查看分区表有多少分区
hive> show partitions dept_partition;
查看分区表结构
desc formatted dept_partition;
创建二级分区并导入
# 创建二级分区
create table dept_partition2(
deptno int, dname string, loc string
) partitioned by (month string, day string) row format delimited fields terminated by '\t';
# 加载数据
load data local inpath '/opt/module/datas/dept.txt'
into table default.dept_partition2 partition(month='201709', day='13');
# 查询分区数据
select * from dept_partition2 where month='201709' and day='13';
把数据直接上传到分区目录上,怎么让分区表和数据产生关联?
1、执行分区修复命令
msck repair table dept_partition2;
2、上传数据后添加分区
alter table dept_partition2 add partition(month='201709', day='11');
3、创建文件夹后 load 数据到分区
load data local inpath '/opt/module/datas/dept.txt'
into table dept_partition2 partition(month='201709',day='10');
4.7 修改表
重命名表
格式:
ALTER TABLE table_name RENAME TO new_table_name
增加/修改/替换列信息
格式:
# 更新列
ALTER TABLE table_name CHANGE [COLUMN] col_old_name
col_new_name column_type [COMMENT col_comment] [FIRST|AFTER column_name]
# 增加和替换列
ALTER TABLE table_name ADD|REPLACE COLUMNS (col_name data_type [COMMENT col_comment], ...)
ADD 是代表新增一字段,字段位置在所有列后面(partition 列前),REPLACE 则是表示替换表中所有字段
4.8 删除表
格式:
DROP TABLE table_name;
5 DML
5.1 数据导入
5.1.1 Load 方式
格式:
load data [local] inpath '/opt/module/datas/student.txt' [overwrite] into table student [partition (partcol1=val1,…)];
- load data 表示加载数据
- local 表示从本地加载数据到hive表;否则从HDFS加载数据到hive表
- inpath 表示加载数据的路径
- overwrite 表示覆盖表中已有数据,否则表示追加
- into table 表示加载到哪张表
- student 表示具体的表
- partition 表示上传到指定分区
5.1.2 Insert 方式
insert overwrite table student1 partition(month = '201708')
select id, name from student where month = '201709';
5.1.3 As Select 方式
create table if not exists student2 as select id, name from student;
5.1.4 Import 方式
import table student3 partition(month='201709') from '/user/hive/warehouse/export/student';
5.1.5 创建表时通过 Location 指定加载数据路径
create external table if not exists student4(
id int, name string
) row format delimited fields terminated by '\t' location '/student;
5.2 数据导出
5.2.1 Insert 导出
将查询结果导出到本地
insert overwrite local directory '/opt/module/datas/export/student'
select * from student;
将查询结果格式化到本地
insert overwrite local directory '/opt/module/datas/export/student' ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
select * from student;
将查询结果导出到 HDFS
insert overwrite directory '/user/atguigu/student2' ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
select * from student;
5.2.2 Hive Shell 命令导出
bin/hive -e 'select * from default.student;' > /opt/module/datas/export/student.txt;
5.2.3 Export 导出到 HDFS
export table default.student to '/user/hive/warehouse/export/student';
5.3 清除表数据
truncate table student;
6 查询
6.1 基本查询
全表查询
select * from emp;
特定列查询
select empno, ename from emp;
列别名
select empno as id, ename as name from emp;
limit
select * from emp limit 5;
6.2 Where
# 查询出薪水等于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);
# 查找以2开头薪水的员工信息
select * from emp where sal like '2%';
# 查找第二个数值为2的薪水的员工信息
select * from emp where sal like '_2%';
# 查找薪水中含有2的员工信息
select * from emp where sal rlike '[2]';
# 查询薪水大于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 (20, 30);
6.3 分组
GROUP BY 语句通常会和聚合函数一起使用,按照一个或者多个列队结果进行分组,然后对每个组执行聚合操作
# 计算emp表每个部门的平均工资
select deptno, avg(sal) from emp group by deptno;
# 计算emp每个部门中每个岗位的最高薪水
Having 只用于 GROUP BY 分组统计语句
# 求每个部门的平均薪水大于2000的部门
select deptno, avg(sal) as avg_sal from emp group by deptno having avg_sal > 2000;
6.4 Join
等值连接
-
根据员工表和部门表中的部门编号相等,查询员工编号、员工名称和部门名称
select e.empno, e.ename, d.deptno, d.dname from emp e join dept d on e.deptno = d.deptno;
内连接
-
只有进行连接的两个表中都存在与连接条件相匹配的数据才会被保留下来
select e.empno, e.ename, d.deptno from emp e join dept d on e.deptno = d.deptno;
左外连接
-
JOIN 操作符左边表中符合 WHERE 子句的所有记录将会被返回
select e.empno, e.ename, d.deptno from emp e left join dept d on e.deptno = d.deptno;
右外连接
-
JOIN操作符右边表中符合WHERE子句的所有记录将会被返回
select e.empno, e.ename, d.deptno from emp e right join dept d on e.deptno = d.deptno;
满外连接
-
将会返回所有表中符合 WHERE 语句条件的所有记录,如果任一表的指定字段没有符合条件的值的话,那么就使用 NULL值替代
select e.empno, e.ename, d.deptno from emp e full join dept d on e.deptno = d.deptno;
多表连接
-
Hive 会对每对 JOIN 连接对象启动一个 MapReduce 任务
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;
6.5 排序
Order By
-
全局排序,只有一个 Reduce
# 查询员工信息按工资升序排列 select * from emp order by sal; # 查询员工信息按工资降序排列 select * from emp order by sal desc;
Sort By
内部排序,对于大规模的数据集 Order By 的效率非常低,在很多情况下,并不需要全局排序,此时可以使用 Sort By
-
Sort by 为每个 Reduce 产生一个排序文件,每个 Reducer 内部进行排序,对全局结果集来说不是排序
# 按照部门编号降序排序 select * from emp sort by deptno desc;
Distribute By
分区排序,在有些情况下,我们需要控制某个特定行应该到哪个 Reducer ,通常是为了进行后续的聚集操作,Distribute By 类似 MR 中 Partition,进行分区,结合 Sort By 使用
对于 Distribute By ,一定要分配多 Reducer 进行处理
Distribute By 的分区规则是根据分区字段的 Hash 值与 Reducer 的个数进行模除后,余数相同的分到一个区
-
Hive 要求 Distribute By 语句要写在 Sort By 语句之前
# 先按照部门编号分区,再按照员工编号降序排序 select * from emp distribute by deptno sort by empno desc;
Cluster By
当 Distribute By 和 Sort By 字段相同时,可以使用 Cluster By 方式
-
Cluster By 除了具有 Distribute By 的功能外还兼具 Sort By 的功能,但是排序只能是升序排序,不能指定排序规则
# 先按照部门编号分区,再按照员工编号升序排列 select * from emp cluster by deptno;
6.6 分桶及抽样
分区提供一个隔离数据和优化查询的便利方式,不过,并非所有的数据集都可形成合理的分区,对于一张表或者分区,Hive 可以进一步组织成桶,也就是更为细粒度的数据范围划分
分桶是将数据集分解成更容易管理的若干部分的另一个技术
分区针对的是数据的存储路径;分桶针对的是数据文件
设置 hive.enforce.bucketing 属性,使得 Hive 能识别桶
set hive.enforce.bucketing = true;
创建分桶表
create table stu_buck (
id int, name string
) clustered by(id) into 4 buckets row format delimited fields terminated by '\t';
查看表结构
desc formatted stu_buck;
导入数据到分桶表中
insert into table stu_buck select id, name from stu;
分桶规则:
Hive 的分桶采用对分桶字段的值进行哈希,然后除以桶的个数求余的方式决定该条记录存放在哪个桶当中
分桶抽样查询
-
对于非常大的数据集,有时用户需要使用的是一个具有代表性的查询结果而不是全部结果,Hive 可以通过对表进行抽样来满足这个需求
语法:TABLESAMPLE(BUCKET x OUT OF y) y必须是table总bucket数的倍数或者因子,y决定抽样的比例,桶的个数除以y为抽样的份数 x必须小于y,决定从哪个桶开始,抽取第x、x+y、x+y+y、...份数据 select * from stu_buck tablesample(bucket 1 out of 4 on id);
7 函数
7.1 空字段赋值
函数说明:
NVL(col, str):给值为 NULL 的数据赋值
# 如果员工的comm为NULL,则用-1代替
select comm, nvl(comm, -1) from emp;
7.2 CASE WHEN
准备数据:
name | dept_id | sex |
---|---|---|
悟空 | 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 '/opt/module/datas/emp_sex.txt' into table emp_sex;
求各部门男女各有多少人
select
dept_id,
sum(case sex when '男' then 1 else 0 end) male,
sum(case sex when '女' then 1 else 0 end) female
from emp_sex group by dept_id;
7.3 行转列
相关函数说明:
CONCAT(string A/col, string B/col…):返回输入字符串连接后的结果
CONCAT_WS(separator, str1, str2,...):将每个字符串用指定的字符分开
COLLECT_SET(col):将某字段的值进行去重汇总,产生 array 类型字段
准备数据:
name | constellation | blood_type |
---|---|---|
孙悟空 | 白羊座 | A |
大海 | 射手座 | A |
宋宋 | 白羊座 | B |
猪八戒 | 白羊座 | A |
凤姐 | 射手座 | A |
创建表并导入数据:
create table person_info(
name string,
constellation string,
blood_type string
) row format delimited fields terminated by "\t";
load data local inpath "/opt/module/datas/constellation.txt" into table person_info;
把星座和血型一样的人归类到一起
select
t.base, concat_ws('|', collect_set(t.name)) name
from
(select name, concat(constellation, ",", blood_type) base from person_info) t
group by
t.base;
7.4 列转行
函数说明:
EXPLODE(col):将 array 或者 map 结构拆分成多行
LATERAL VIEW
用法:LATERAL VIEW udtf(expression) tableAlias AS columnAlias
解释:用于和 split, explode 等UDTF一起使用,它能够将一列数据拆成多行数据,在此基础上可以对拆分后的数据进行聚合
准备数据:
movie | category |
---|---|
《疑犯追踪》 | 悬疑,动作,科幻,剧情 |
《Lie to me》 | 悬疑,警匪,动作,心理,剧情 |
《战狼2》 | 战争,动作,灾难 |
创建表并导入数据:
create table movie_info(
movie string,
category array
) row format delimited fields terminated by "\t" collection items terminated by ",";
load data local inpath "/opt/module/datas/movie.txt" into table movie_info;
将电影分类中的数组数据展开
select
movie,
item
from
movie_info lateral view explode(category) t as item;
7.5 开窗
相关函数说明:
OVER():指定分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变而变化
CURRENT ROW:当前行
n PRECEDING:往前 n 行数据
n FOLLOWING:往后 n 行数据
UNBOUNDED:起点,UNBOUNDED PRECEDING 表示从前面的起点, UNBOUNDED FOLLOWING 表示到后面的终点
LAG(col,n,default_val):往前第 n 行数据
LEAD(col,n, default_val):往后第 n 行数据
NTILE(n):把有序分区中的行分发到指定数据的组中,各个组有编号,编号从 1 开始,对于每一行,NTILE 返回此行所属的组的编号
创建表并导入数据
create table business(
name string,
orderdate string,
cost int
) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';
load data local inpath "/opt/module/datas/business.txt" into table business;
按需求查询数据
# 查询在2017年4月份购买过的顾客及总人数
select name, count(*) over() from business where substring(orderdate, 1, 7) = '2017-04';
# 查询顾客的购买明细及月购买总额
select name, orderdate, sum(cost) over(partition by month(orderdate)) from business;
# 上述的场景, 将每个顾客的cost按照日期进行累加
select
name,
orderdate,
cost,
sum(cost) over(partition by name
order by
orderdate rows between unbounded preceding and current row)
from
business;
# 查询每个顾客上次的购买时间
select
name,
orderdate,
cost,
lag(orderdate, 1) over(partition by name)
from
business;
# 查询前20%时间的订单信息
select
*
from
(
select
name,
orderdate,
ntile(5) over(
order by orderdate) sorted
from
business) t
where
sorted = 1;
7.6 Rank
函数说明:
RANK() 排序相同时会重复,总数不会变
DENSE_RANK() 排序相同时会重复,总数会减少
ROW_NUMBER() 会根据顺序计算
数据准备:
name | subject | score |
---|---|---|
孙悟空 | 语文 | 87 |
孙悟空 | 数学 | 95 |
孙悟空 | 英语 | 68 |
大海 | 语文 | 94 |
大海 | 数学 | 56 |
大海 | 英语 | 84 |
宋宋 | 语文 | 64 |
宋宋 | 数学 | 86 |
宋宋 | 英语 | 84 |
婷婷 | 语文 | 65 |
婷婷 | 数学 | 85 |
婷婷 | 英语 | 78 |
创建表并导入数据:
create table score(
name string,
subject string,
score int
) row format delimited fields terminated by "\t";
load data local inpath '/opt/module/datas/score.txt' into table score;
计算每门学科成绩排名
select
name,
subject,
score,
rank() over (partition by subject
order by
desc)
from
score;
计算没门学科前三名
select
*
from
(
select
*,
rank() over (partition by subject
order by
score desc) as rk
from
score) t
where
t.rk <= 3;
7.7 自定义函数
根据用户自定义函数类别分为以下三种:
- UDF(User-Defined-Function)一进一出
- UDAF(User-Defined Aggregation Function)聚集函数,多进一出
- UDTF(User-Defined Table-Generating Functions)一进多出
步骤:
1、导入依赖
org.apache.hive
hive-exec
1.2.1
2、创建一个类
package com.djm.hive;
import org.apache.hadoop.hive.ql.exec.UDF;
public class Lower extends UDF {
public String evaluate (final String s) {
if (s == null) {
return null;
}
return s.toLowerCase();
}
}
3、打成 jar 包上传到服务器 /opt/module/jars/ 目录
4、将 jar 包添加到 hive 的 classpath
add jar /opt/module/datas/udf.jar;
5、创建临时函数与 Class 关联
create temporary function mylower as "com.djm.hive.Lower";
6、使用自定义函数
select ename, mylower(ename) lowername from emp;
8 压缩和存储
8.1 开启 Map 输出阶段压缩
开启 map 输出阶段压缩可以减少 job 中 map 和 Reduce task 间数据传输量
# 开启hive中间传输数据压缩功能
set hive.exec.compress.intermediate=true;
# 开启mapreduce中map输出压缩功能
set mapreduce.map.output.compress=true;
# 设置mapreduce中map输出数据的压缩方式
set mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.SnappyCodec;
8.2 开启 Reduce 输出阶段压缩
当 Hive 将输出写入到表中时,输出内容同样可以进行压缩
# 开启hive最终输出数据压缩功能
set hive.exec.compress.output=true;
# 开启mapreduce最终输出数据压缩
set mapreduce.output.fileoutputformat.compress=true;
# 设置mapreduce最终数据输出压缩方式
set mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.SnappyCodec;
# 设置mapreduce最终数据输出压缩为块压缩
set mapreduce.output.fileoutputformat.compress.type=BLOCK;
8.3 文件存储格式
8.3.1 列式存储和行式存储
Hive 支持的存储数据的格式主要有:TEXTFILE 、SEQUENCEFILE、ORC、PARQUET
行存储的特点:
查询满足条件的一整行数据的时候,列存储则需要去每个聚集的字段找到对应的每个列的值,行存储只需要找到其中一个值,其余的值都在相邻地方,所以此时行存储查询的速度更快。
列存储的特点:
因为每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量;每个字段的数据类型一定是相同的,列式存储可以针对性的设计更好的设计压缩算法。
8.3.2 TextFile 格式
默认格式,数据不做压缩,磁盘开销大,数据解析开销大,可结合 Gzip、Bzip2 使用,但使用 Gzip 这种方式,hive 不会对数据进行切分,从而无法对数据进行并行操作
8.3.3 Orc 格式
Orc (Optimized Row Columnar)是Hive 0.11版里引入的新的存储格式
如图所示可以看到每个 Orc 文件由 1 个或多个 Stripe 组成,每个 Stripe 一般为 HDFS 的块大小,每一个 Stripe 包含多条记录,这些记录按照列进行独立存储,对应到 Parquet 中的 row group的概念,每个 Stripe 里有三部分组成,分别是 Index Data,Row Data,Stripe Footer:
Index Data:一个轻量级的 index,默认是每隔 1W 行做一个索引,这里做的索引应该只是记录某行的各字段在 Row
Data 中的 offsetRow Data:存的是具体的数据,先取部分行,然后对这些行按列进行存储。对每个列进行了编码,分成多个 Stream 来存储
Stripe Footer:存的是各个 Stream 的类型,长度等信息
每个文件有一个 File Footer,这里面存的是每个 Stripe 的行数,每个 Column 的数据类型信息等;每个文件的尾部是一个PostScript,这里面记录了整个文件的压缩类型以及FileFooter的长度信息等,读取文件时,会 seek 到文件尾部读PostScript,从里面解析到 File Footer 长度,再读 FileFooter,从里面解析到各个 Stripe 信息,再读各个 Stripe,即从后往前读
8.3.4 Parquet 格式
如图所示一个文件中可以存储多个行组,文件的首位都是该文件的 Magic Code,用于校验它是否是一个 Parquet 文件,Footer length 记录了文件元数据的大小,通过该值和文件长度可以计算出元数据的偏移量,文件的元数据中包括每一个行组的元数据信息和该文件存储数据的 Schema 信息,除了文件中每一个行组的元数据,每一页的开始都会存储该页的元数据,在Parquet 中,有三种类型的页:数据页、字典页和索引页,数据页用于存储当前行组中该列的值,字典页存储该列值的编码字典,每一个列块中最多包含一个字典页,索引页用来存储当前行组下该列的索引,目前 Parquet 中还不支持索引页。
Parquet 文件是以二进制方式存储的,所以是不可以直接读取的,文件中包括该文件的数据和元数据,因此 Parquet 格式文件是自解析的。
Row Group:每一个行组包含一定的行数,在一个 HDFS 文件中至少存储一个行组,类似于 Orc 的 Stripe 的概念
Column Chunk:在一个行组中每一列保存在一个列块中,行组中的所有列连续的存储在这个行组文件中,一个列块中的值都是相同类型的,不同的列块可能使用不同的算法进行压缩
Page:每一个列块划分为多个页,一个页是最小的编码的单位,在同一个列块的不同页可能使用不同的编码方式
通常情况下,在存储 ParquetBlockMapperBlock 每一个行组由一个任务处理,增大任务执行并行度
8.4 存储和压缩结合
查看是否支持 Snappy
hadoop checknative
将编译好的支持 Snappy 压缩的 Hadoop 解压
tar -zxvf hadoop-2.7.2.tar.gz
拷贝 /opt/software/hadoop-2.7.2/lib/native 到 /opt/module/hadoop-2.7.2/lib/native
cp ../native/* /opt/module/hadoop-2.7.2/lib/native/
集群分发
xsync /opt/module/hadoop-2.7.2/lib/native/
再次查看是否支持 Snapy