【大数据】Hadoop里的“MySQL”——Hive,干货满满
- 文章脉络
- Hive架构
- HQL
- 表类型
- 创建表语法
- 分区
- 数据导入导出
- 函数
- 内置函数
- UDF
- Java
- Python
在阅读本文前,请确保已经对Hadoop的三大组件(HDFS、MapReduce、YARN)有所了解,如果不太了解的可以移步《深入浅出Hadoop》观看。
如果仅使用MapReduce来操作HDFS中存放的数据,那么学习成本和开发成本都会比较高。于是,Hive的提出就向广大开发者提供了一种利用SQL来查询和计算HDFS文件的方法。
文章脉络
Hive架构
Hive作为Hadoop生态系统中的重要一员,它本质上是一个数仓工具。其核心功能是将存储在Hadoop分布式文件系统(HDFS)上的结构化数据文件映射为数据库中的表结构。这一映射过程允许用户通过一种类似SQL的查询语言——即Hive Query Language(HQL)——来执行数据查询和管理操作,极大地简化大数据的处理流程。
在Hadoop生态系统中,数据文件通常以原始格式存放在HDFS上。如果我们想要对这些数据进行查询和计算,按照传统方法,我们需要编写复杂的MapReduce程序。MapReduce作为一种编程模型,虽然功能强大,但其学习成本较高,编写和维护MapReduce脚本的难度较大,这无疑增加了学习成本和时间成本,对于非程序员或数据分析人员来说尤其如此。
Hive的出现,为大数据处理提供了一种更加简便的方法。它允许用户使用类似SQL的语句来查询数据,而这些SQL语句在Hive内部会被转换成相应的MapReduce程序。这意味着,有了Hive,即使是不熟悉MapReduce编程的用户也可以轻松地执行复杂的数据分析任务,无需直接编写繁琐的MapReduce代码。
【注意】:尽管Hive在Hadoop生态系统中扮演着重要角色,但它的定位主要是作为一个数据仓库工具,而不是一个需要独立集群部署的服务。它只是一个把SQL解释成MapReduce代码然后提交任务的工具,是不需要集群部署的。
在架构上,Hive的底层仍然依赖于MapReduce计算框架、YARN资源管理器和HDFS文件系统。需要注意的是,虽然HQL与标准SQL在语法上有很多相似之处,但它并不是完全兼容SQL的。Hive的SQL方言因包含一些特有的功能和限制,因此被称为HQL。
【注意】:不知道大家开发Java Web项目的时候有没有用过SSH架构?Hibernate与MyBatis是地位等同的框架, 当时基于Hibernate开发的SQL一度也称为HQL,注意不要与Hive SQL的简称HQL弄混了。
HQL和SQL在设计和语法上都有一些差异,可以理解为它们俩有交集(这个交集其实就是大家平常最通用的功能,比如select * from table)。HQL不支持事务但是在查询的写法上比较强大,而SQL面向事务性数据库设计,适用于实时操作,但是写法不像HQL这么五花八门。
在图2中,我们可以看到一个清晰的架构视图,其中4个最重要的组件如下:
1、用户接口Client:Hive提供了多种用户接口,以适应不同用户的需求和使用场景。
首先是CLI(命令行接口),它允许用户通过命令行直接与Hive交互,执行HQL语句和查看查询结果。
其次是JDBC/ODBC接口,这使得Hive能够被任何支持JDBC或ODBC标准的第三方应用程序所访问,比如商业智能(BI)工具和定制的数据分析应用程序。
【注意】:BI,Business Intelligence,一般是公司内部部门下的一个团队,负责处理部门内部数据与外部对接、数据可视化等等。比如你是某公司外卖事业部的开发人员,想要获取你司单车部门用户骑行数据,那么你需要去找单车部门的BI对接数据。
2、元数据Metastore:元数据是Hive中关于数据的数据。它包含了数据库、表、列、分区、用户权限以及其他与Hive管理的数据相关的信息。这些元数据存储在Metastore中,它可以是嵌入式的Derby数据库,也可以是独立的MySQL数据库。推荐使用MySQL来存储元数据,因为它支持多用户并发访问,且具有更好的可扩展性和稳定性。通过维护这些元数据,Hive能够跟踪数据的结构和位置,从而在执行查询时能够正确地定位和处理数据。
【注意】:Hive会把结构化数据解析为Hive表,那么这些表的数据如果再存放在HDFS,查一下表信息就要等一段时间,就没法管理了。因此是存在DB中,保证用户体验。
3、Thrift:Thrift是一个跨语言的服务框架,它允许客户端使用不同的编程语言与Hive进行通信。这意味着,无论开发者使用Java、Python、C++还是PHP等语言,他们都可以通过Thrift定义的接口与Hive进行交互。这种跨语言支持极大地提高了Hive的可用性和灵活性,使得它能够被集成到各种不同的应用和服务中。
【注意】:工作中,算法和数仓人员都会用Python,如果你再偏工程点,Java也会要写。因此等你到实际生产开发中就会发现这些工具为什么要跨语言。。。。因为你极有可能今天这个项目是用Python写的,下个项目是用Java写的,下下个项目又跳到C++了。最近感觉学习能力比专业能力更重要,任务完成是目的,而语言只是手段。
4、驱动器Driver:驱动器是Hive的核心组件之一,负责处理用户提交的查询请求。主要工作是解析HQL,转换为MapReduce(MR)或Spark作业提交。
【注意】:Spark地位等同于MapReduce,但是比MapReduce快很多。最初Hive的执行引擎是MapReduce,后来Spark也兼容了Hive。因此现在写HQL都使用Spark作为执行引擎,不再用MapReduce了。
HQL
HQL中有一些让我初次接触时让我比较惊艳的设计和概念,所以这里着重介绍一下它比MySQL更惊艳的地方。HQL基本语法和SQL一致,这里不会介绍。
表类型
在Hive中,根据数据的处理和管理方式,可以将表分为内部表(也称为管理表)和外部表。
内部表:内部表是Hive中最为常见的一种表类型,由Hive完全管理。当我们在Hive中创建一个内部表时,表的数据实际上被存储在Hive所管理的HDFS上的特定位置。当我们执行删除内部表的命令时,Hive不仅会从其元数据中删除表的记录,而且还会删除在HDFS上与该表关联的所有数据文件。
【注意】:内部表的存储地址是
/user/hive/warehouse/your_db_name.db/table_name
。
外部表:在Hive中创建外部表时,指定一个指向HDFS上数据文件的外部位置,但Hive并不拥有这些数据。这意味着,当我们删除一个外部表时,Hive仅会从其元数据存储中移除表的元数据信息,而不会删除HDFS上的实际数据文件。
创建表语法
在用Hive创建表的时候,需要指定一些有趣的分隔符。
下面是一个创建表的基本例子,展示了如何创建一个内部表:
CREATE TABLE [IF NOT EXISTS] table_name (
column1_name '字段注释',
column2_name '字段注释',
...
columnN_name '字段注释'
)
[COMMENT '表注释']
[ROW FORMAT DELIMITED]
[FIELDS TERMINATED BY char]
[STORED AS file_format];
和MySQL比较不同的是下面三个:
- ROW FORMAT DELIMITED: 指定表的行格式。这通常用于指定字段之间的分隔符。
- FIELDS TERMINATED BY char: 指定字段之间的分隔符。
- STORED AS file_format: 指定表的存储格式。常见的存储格式包括 TEXTFILE
, SEQUENCEFILE
, ORC
, 和 PARQUET
。
也就是说,在创建表时,可以指定每个字段之间的分隔符、行之间的分隔符和文件类型。
【注意】:这不就是excel、csv嘛!你可以直接下载下来然后用pandas加载,只是有可能数据量巨大,你的内存会爆。
分区
分区表实际上就是把一个表在HDFS上的文件建立独立的文件夹,每个文件夹对应表的一个分区;相当于把一个大数据集分割成了许多小数据集,这样做可以把数据管理的很有条理。并且在使用where
查询并且指定分区的时候,查询效率也会增加很多。
创建分区表的语法:
create table 表(
字段名 数据类型,
...
)
partitioned by (字段 数据类型)
row format delimited fields terminated by ',';
数据导入导出
Hive中的数据导入与导出有非常多种写法,并且这些写法在不同的场景下很有用,下面列出具有代表性的几个。
一、Insert语句把Hive表导出到HDFS
insert overwrite directory
'hdfs://user/opt/module/hive-3.1.2/datas/'
select * from student1;
二、HIVE Shell命令把Hive表导出到HDFS
hive -e "select * from db_hive.student1" > 0000.txt
【注意】:Insert语句是在Hive的Cli环境下写的。Hive Shell可以在bash环境里写。
一、insert导入HDFS文件到Hive表
load data inpath 'hdfs://xxx/xxx/student.txt'
overwrite into table student4;
二、insert根据HQL查询结果导入到Hive表
insert overwrite table student5
select id,name from student1;
三、create as 根据查询结果创建
create table if not exists student6
as
select id,name from student1;
四、通过location指定表的源数据
create external table if not exists student6(
id int,
name string
)
row format delimited fields terminated by ','
location '/export/student/data';
【注意】:insert语句分别支持外部文件和查询结果导入,create as 是直接根据查询结果先创建再导入,location是把表关联到一个地址去。写法五花八门,主要还是一看场景、二看怎么做方便工作量小。
函数
Hive在查询时,除了可以用普通SQL之外,还可以加入函数来处理字段。Hive的函数除了内置函数,还有UDF(用户自定义函数)。UDF非常滴炫酷。
内置函数
内置函数SQL也有,最简单的有MIN
、MAX
这些,Hive里比较高级的内置函数如下:
collect_list
是一个聚合函数,用于将指定列的所有值收集到一个列表中。当你需要对某一列的所有值进行集合操作时,这个函数非常有用。例如,如果你想要获取一个部门所有员工的姓名列表,可以使用 collect_list
来实现。
SELECT department, collect_list(name) as employee_names
FROM employees
GROUP BY department;
lateral view
是一个非常有用的操作,它允许你将一个表的列值拆分成多行,并与原始表的行进行连接。这在处理数组或映射类型的列时特别有用。结合 explode
函数,你可以将数组或映射中的每个元素都转换成单独的一行。
SELECT id, name, salary
FROM employees
LATERAL VIEW explode(skills) AS skill;
explode
是一个UDTF(User-Defined Table-Generating Function,用户定义的表生成函数),它用于将数组或映射类型的单个列拆分成多行。explode
通常与 lateral view
一起使用,以便将数组或映射中的每个元素转换成单独的一行。
SELECT id, skill
FROM employees
LATERAL VIEW explode(skills) AS skill;
concat
是一个字符串函数,它用于将多个字符串连接成一个单一的字符串。当你需要将多个列的值合并成一个字符串时,concat
函数非常有用。
SELECT concat(first_name, ' ', last_name) as full_name
FROM employees;
concat_ws
是 concat
的一个变体,它允许你指定一个分隔符来连接字符串。这在连接列值时提供了更多的灵活性,特别是当你需要在一个特定的分隔符之间插入值时。
SELECT concat_ws('-', first_name, middle_name, last_name) as full_name
FROM employees;
UDF
UDF允许用户自己定义封装函数,然后调用处理,最常用的就是Java、Python来实现。
Java
首先,你需要继承Hive提供的类。对于UDF,可以继承 org.apache.hadoop.hive.ql.udf.generic.GenericUDF
类;这个类提供了实现函数的基本框架,包括输入参数的类型检查、执行逻辑的实现等。
在继承的类中,你需要实现几个抽象方法,这些方法定义了函数的行为。对于UDF,你需要实现 evaluate()
方法。这个方法是函数执行的核心,你需要在这里编写具体的逻辑。
一旦你的函数实现完成,你需要将它们打包成jar文件,然后将这个jar文件上传到Hive的 lib
目录下。
在Hive命令窗口中,你可以通过以下命令来创建函数:
# 添加jar包
add jar 自定义jar包的路径;
# 创建Function,temporary表示添加临时的function
create [temporary] function [if not exists] 函数名称 "自定义函数的全类名";
# 删除函数
drop [temporary] function [if exists] 函数名称;
一旦函数创建成功,你就可以在Hive查询中使用它了。例如,如果你创建了一个名为 my_udf
的UDF,你可以在查询中这样使用:
SELECT my_udf(column1), column2
FROM table;
【注意】:UDF其实有三种:UDF、UDAF、UFTF,分别表示“一对一”、“多对一”、“一对多”。这里只介绍UDF,主要是和Python的写法比较一下。
Python
Python 实现的 UDF、UDAF、UDTF 函数,需要对输入的一整行的数据进行处理,而并不是针对某一个字段进行处理!
读取每一行的输入:可以使用 Python 中的标准输入流来读取。需要注意的是,无论 Hive 的表中是以什么样的字符进行分隔的,这里读取到的字段之间始终是以\t
进行分隔的!因此我们也必须要使用\t
进行字段的切割。
输出一行的数据:在将一整行的数据处理完成之后,如果需要输出的时候,直接使用 print 函数打印即可。与输入的一样,输出的不同的字段之间也必须要使用\t
进行分隔!
UDAF 函数实现、UDTF 函数实现:如果实现 UDAF、UDTF 这种输入行和输出行不一致的情况,直接控制 print 函数的输出次数即可。一次的 print 就会生成一行的数据,因此合理控制 print 函数的调用次数,即可实现 UDAF、UDTF 函数。
-- 应用UDF
add file example.py
-- 处理一列
select transform(t_name) using 'python3 example.py' as (t_name) from example_01;
-- 查询两列
select transform(t_name, t_gender) using 'python3 example.py' as (t_name, t_gender) from example_01;
【注意】:Java的UDF和Python的UDF调用时非常不一样,要牢记。