大家好,我是脚丫先生 (o^^o)
今天想和小伙伴们聊一聊大数据之数据仓库 Hive。
大数据工作就目前而言,数仓工程师和平台开发工程师岗位比较多。
我毕业后,一直从事着平台开发工作。
不管是什么方向,Hive 的快速学习都尤为重要。
前言
Hive 数据仓库在 Hadoop 的生态家族中占有及其重要的地位,并且实际的业务当中用的也非常多,可以说 Hadoop 之所以这么流行在很大程度上是因为 Hive 的存在。
特别是针对离线数据仓库业务来说,基本都是在以 hive 为基础,进行分层设计,辅以调度系统,从而完成整个数据仓库业务的定时执行。
一、Hive 基本概念
1.1 什么是 Hive
Hive:它是由 Facebook 开源用于解决海量结构化日志的数据统计。它是基于大数据生态圈 hadoop 的一个数据仓库工具,可以将结构化的数据文件映射为一张表,并提供类 SQL 查询功能。
其本质是将 HQL 转化成 MapReduce 程序。
从图示可以看出,Hive 从某种程度上讲就是很多“SQL—MapReduce”框架的一个封装,可以将用户编写的 Sql 语言解析成对应的 MapReduce 程序,最终通过 MapReduce 运算框架形成运算结果提交给 Client。
1.2 Hive 的优缺点
优点:
- ① 操作接口采用类 SQL 语法,提供快速开发的能力。
- ② 避免了去写 MapReduce,减少开发人员的学习成本。
- ③ Hive 的执行延迟比较高,因此 Hive 常用于数据分析,对实时性要求不高的场合。
- ④ Hive 优势在于处理大数据,对于处理小数据没有优势,因为 Hive 的执行延迟比较高。
- ⑤ Hive 支持用户自定义函数,用户可以根据自己的需求来实现自己的函数。
缺点:
- ① Hive 的 HQL 表达能力有限。
- ② 迭代式算法无法表达。
- ③ Hive 的效率比较低
- ④ Hive 自动生成的 MapReduce 作业,通常情况下不够智能化。
- ⑤ Hive 调优比较困难,粒度较粗。
1.3 Hive 架构原理
1.用户接口:Client
CLI(hive shell)、JDBC/ODBC(java 访问 hive)、WEBUI(浏览器访问 hive)。
2.元数据:Metastore
元数据包括:表名、表所属的数据库(默认是 default)、表的拥有者、列/分区字段、表的类型(是否是外部表)、表的数据所在目录等。
Hive 将表中的元数据信息存储在数据库中,如 derby(自带的)、Mysql(实际工作中配置的),Hive 中的元数据信息包括表的名字、表的列和分区、表的属性(是否为外部表等)、表的数据所在的目录等。
Hive 中的解析器在运行的时候会读取元数据库 MetaStore 中的相关信息。
在这里和大家说一下为什么在实际业务当中不用 Hive 自带的数据库 derby,而要重新为其配置一个新的数据库 Mysql,是因为 derby 这个数据库具有很大的局限性:derby 这个数据库不允许用户打开多个客户端对其进行共享操作,只能有一个客户端打开对其进行操作,即同一时刻只能有一个用户使用它,自然这在工作当中是很不方便的,所以我们要重新为其配置一个数据库。
3.Hadoop 使用 HDFS 进行存储,使用 MapReduce 进行计算。
4.驱动器:Driver
- ① 解析器(SQL Parser):将 SQL 字符串转换成抽象语法树 AST,这一步一般都用第三方工具库完成,比如 antlr;对 AST 进行语法分析,比如表是否存在、字段是否存在、SQL 语义是否有误。
- ② 编译器(Physical Plan):将 AST 编译生成逻辑执行计划。
- ③ 优化器(Query Optimizer):对逻辑执行计划进行优化。
- ④ 执行器(Execution):把逻辑执行计划转换成可以运行的物理计划。对于 Hive 来说,就是 MR/Spark。
从上面的体系结构中可以看出,在 Hadoop 的 HDFS 与 MapReduce 以及 MySql 的辅助下,Hive 其实就是利用 Hive 解析器将用户的 SQl 语句解析成对应的 MapReduce 程序而已,即 Hive 仅仅是一个客户端工具,这也是为什么我们在 Hive 的搭建过程中没有分布与伪分布搭建的原因。(Hive 就像是刘邦一样,合理的利用了张良、韩信与萧何的辅助,从而成就了一番大事!)。
1.4 Hive 运行机制
Hive 的运行机制正如图所示:创建完表之后,用户只需要根据业务需求编写 Sql 语句,而后将由 Hive 框架将 Sql 语句解析成对应的 MapReduce 程序,通过 MapReduce 计算框架运行 job,便得到了我们最终的分析结果。
在 Hive 的运行过程中,用户只需要创建表、导入数据、编写 Sql 分析语句即可,剩下的过程将由 Hive 框架自动完成,而创建表、导入数据、编写 Sql 分析语句其实就是数据库的知识了,Hive 的运行过程也说明了为什么 Hive 的存在降低了 Hadoop 的学习门槛以及为什么 Hive 在 Hadoop 家族中占有着那么重要的地位。
二、Hive 的操作
在初步了解 Hive 的基本概念之后,我们将对 Hive 进行实践操作。所谓“纸上得来终觉浅,绝知此事要躬行”。首先建立一个 txt 格式的文本,数据如下所示:
id city name sex
1 beijing zhangli man
2 guizhou lifang woman
3 tianjin wangwei man
4 chengde wanghe woman
5 beijing lidong man
6 lanzhou wuting woman
7 beijing guona woman
8 chengde houkuo man
Hive 的操作对于用户来说实际上就是表的操作、数据库的操作。
下面将围绕两个方面进行介绍。
2.1 Hive 表——内部表、外部表、分区表的创建
所谓内部表就是普通表,创建语法格式为:
create table tablename #内部表名
(
id int, #字段名称,字段类型
city string,
name string,
sex string
)
row format delimited #一行文本对应表中的一条记录
fields terminated by ‘\t’#指定输入文件字段的间隔符,即输入文件的字段是用什么分割开的。
实际操作(用 beeline 客户端模式):
beeline -u jdbc:hive2://node1:10000 -n "用户名" -p "密码"
结果:
外部表(external table)的创建语法格式为:
create external table teblename #外部表名
(
id int,
city string,
name string,
sex string
)
row format delimited #一行文本对应一条记录
fields terminated by ‘\t’ #输入文件的字段是用什么分割开的。
location ‘hdfs://mycluster/testDir’#与hdfs中的文件建立链接。
注意:最后一行写到的是目录testDir,文件就不用写了,Hive表会自动testDir目录下读取所有的文件file。实际的操作过程当中发现,location关联到的目录下面必须都是文件,不能含有其余的文件夹,不然读取数据的时候会报错。
实际操作:
内部表与外部表的区别:
内部表在加载数据的过程中,实际数据会被移动到数据仓库目录中(hive.metastore.warehouse.dir),之后用户对数据的访问将会直接在数据仓库目录中完成;删除内部表时,内部表中的数据和元数据信息会被同时删除。
外部表在加载数据的过程中,实际数据并不会被移动到数据仓库目录中,只是与外部表建立一个链接(相当于文件的快捷方式一样);删除外部表时,仅删除该链接。补充:在工作中发现,对于外部表,即使 hive 中的表删除了,但是在 HDFS 中表的 location 仍然存在。
分区表
分区表的概念:指的是我们的数据可以分区,即按照某个字段将文件划分为不同的标准,分区表的创建是通过在创建表时启用 partitioned by 来实现的。
分区表的创建语法格式为:
create table tablename #分区表名
(
id int, #字段名称 字段类型
city string,
name string,
sex string
)
partitioned by(day int) #分区表字段
row format delimited #一行文本对应一条记录
fields terminated by ‘\t’ #输入文件的字段是用什么分割开的。
注意:分区表在加载数据的过程中要指定分区字段,否则会报错,正确的加载方式如下:
load data local inpath ‘/usr/local/consumer.txt’ into table t1 partition (day=2);
其余的操作和内部表、外部表是一样的。
实际操作:
2.2 将数据文件加载(导入)到 Hive 表中
在 Hive 中创建完表之后,随后自然要向表中导入数据,但是在导入数据的时候和传统数据库是不同的:Hive 不支持一条一条的用 insert 语句进行插入操作,也不支持 update 的操作。Hive 表中的数据是以 load 的方式,加载到建立好的表中。数据一旦导入,则不可修改。要么 drop 掉整个表,要么建立新的表,导入新的数据。导入数据的语法格式为:
导入数据时要注意以下几点:
- local inpath 表示从本地 linux 中向 Hive 表中导入数据,inpath 表示从 HDFS 中向 Hive 表中导入数据。
- 默认是向原 Hive 表中追加数据,overwrite 表示覆盖表中的原数据进行导入。
- partition 是分区表特有的,而且在导入数据数据时是必须添加的,否则会报错。
- load 操作只是单纯的复制/移动操作,将数据文件复制/移动到 Hive 表对应的位置,即 Hive 在加载数据的过程中不会对数据本身进行任何修改,而只是将数据内容复制或者移动到相应的表中。
三、Hive 函数
3.1 系统内置函数:
- 查看系统自带的函数
hive> show functions;
- 显示自带的函数的用法
desc function upper;
- 详细显示自带的函数的用法
desc function extended upper;
3.2 系统内置常用函数:
- 数学函数
- ① round 四舍五入函数。
- ② ceil 向上取整函数。
- ③ sqrt 求平方根函数。
- ④ abs 求绝对值函数。
- ⑤ greatest 求一组数据中的最大值。
- ⑥ least 求一组数据的最小值。
- ⑦ cast 转换数据类型,成功返回结果,否则返回 Null。
- 字符串函数
- ① trim 去空格函数。
- ② ltrim 左边去空格函数
- ③ rtrim 右边去空格函数。
- ④ concat_ws (separator,str1,str2,...) concat_ws 第一个参数是其它参数的分隔符,分隔符的位置放在要连接的两个字符串之间,分隔符可以是一个字符串,也可以是其他参数。
3.3 自定义函数
- Hive 自带了一些函数,比如:max/min 等,但是数量有限,自己可以通过自定义 UDF 来方便的扩展。
- 当 Hive 提供的内置函数无法满足你的业务处理需要时,此时就可以考虑使用用户自定义函数(UDF:user-defined function)。
根据用户自定义函数类别分为以下三种:
- ① UDF(User-Defined-Function)一进一出。
- ② UDAF(User-Defined Aggregation Function)聚集函数,多进一出
类似于:count/max/min。 - ③ UDTF(User-Defined Table-Generating Functions)一进多出,如 lateral view explore()。
- 官方文档地址
https://cwiki.apache.org/confluence/display/Hive/HivePlugins - 编程步骤:
- ① 继承 org.apache.hadoop.hive.ql.UDF
- ② 需要实现 evaluate 函数;evaluate 函数支持重载;
- ③ 在 hive 的命令行窗口创建函数
a)添加 jar
add jar linux_jar_path
b) 创建 function
create [temporary] function [dbname.]function_name AS class_name;
④ 在 hive 的命令行窗口删除函数
drop [temporary] function [if exists] [dbname.]function_name;
- 注意事项
- ① UDF 必须要有返回类型,可以返回 null,但是返回类型不能为 void;
3.4 自定义 UDF 函数
- 创建一个 Maven 工程 Hive
- 导入依赖
<dependencies>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>1.2.1</version>
</dependency>
</dependencies>
- 创建一个类
package com.atguigu.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();
}
}
- 打成 jar 包上传到服务器/usr/local/testJar/udf.jar。
- 将 jar 包添加到 hive 的 classpath。
hive(default)> add jar /usr/local/testJar/udf.jar;
- 创建临时函数与开发好的 java class 关联
hive(default)>create temporary function mylower as "com.atguigu.hive.Lower";
- 即可在 hql 中使用自定义的函数 mylower
hive(default)> select ename, mylower(ename) lowername from emp;
3.5 分析函数
分析函数:row_number() over()——分组 TOPN
需求: 需要查询出每种性别中年龄最大的 2 条数据,数据如下
1,18,a,male
2,19,b,male
3,22,c,female
4,16,d,female
5,30,e,male
6,26,f,female
实际操作: 使用 row_number 函数,对表中的数据按照性别分组,按照年龄倒序排序并进行标记
select id,age,name,sex,
row_number() over(partition by sex order by age desc) as rank
from t_rownumber;
结果:
之后,利用上面的结果,查询出 rank<=2 的即为最终需求:
select id,age,name,sex
from
(select id,age,name,sex,
row_number() over(partition by sex order by age desc) as rank
from t_rownumber) tmp
where rank<=2;
结果:
3.6 转列函数
函数:explode()。数据如下
1,zhangsan,化学:物理:数学:语文
2,lisi,化学:数学:生物:生理:卫生
3,wangwu,化学:语文:英语:体育:生物
1.映射成一张表
create table t_stu_subject(id int,name string,subjects array<string>)
row format delimited fields terminated by ','
collection items terminated by ':';
2.导入数据
load data local inpath '/usr/local/testJar/subject.txt' overwrite into table t_stu_subject;
3 用 explode()对数组字段“炸裂”
之后,利用这个 explode 的结果,来求去重的课程:
select distinct tmp.sub
from
(select explode(subjects) as sub from t_stu_subject) tmp;
结果:
四、综合案例
4.1 利用 HQL 去做统计
需要用 hive 做 wordcount,有以下文本文件 word.txt,
hello tom hello jim
hello rose hello tom
tom love rose rose love jim
jim love tom love is what
what is love
1.创建表
create table t_wc(sentence string);
2.导入数据
load data local inpath '/usr/local/testJar/word.txt' overwrite into table t_wc;
3.语句查询
SELECT word
,count(1) as cnts
FROM (
SELECT explode(split(sentence, ' ')) AS word
FROM t_wc
) tmp
GROUP BY word
order by cnts desc;
结果展示
至此,数据仓库Hive讲解完毕。
今天就聊到这里,祝各位终有所成,收获满满!
我是脚丫先生,我们下期见~