EDUCBA-Hive-Pig-MapReduce-大数据分析笔记-全-
EDUCBA Hive、Pig、MapReduce 大数据分析笔记(全)
001:Hive简介 🐝
在本节课中,我们将要学习Apache Hive的基础知识。我们将探讨Hive是什么、它的特性、架构、工作原理、支持的数据类型以及其数据模型。通过本教程,你将能够理解Hive如何作为Hadoop之上的数据仓库工具,用于数据汇总、查询和分析。
什么是Hive?🤔
Hive是一个构建在Hadoop之上的数据仓库工具,用于提供数据汇总、查询和分析功能。在Hive中,我们可以存储数据,对数据进行汇总,然后通过查询获取所需数据并进行分析。
Hive的特性包括:
- Hive始于2007年的Facebook。
- Apache Hive支持对存储在Hadoop HDFS和HBase中的大型数据集进行分析。
- 它专为OLAP(在线分析处理)设计,使用户能够轻松地从不同角度提取和查看数据。
- 在Hive中,我们可以从不同来源提取数据,并将其存储在HDFS和HBase中。
- 为了分析数据,Hive提供了一种类似SQL的语言,称为HiveQL。它采用“读时模式”,并将查询透明地转换为MapReduce作业,在Hadoop集群上执行。
- Hive能够为各种数据格式带来结构,支持文本、序列、ORC、RC文件和Apache Parquet等格式。
- 默认情况下,Hive将元数据存储在嵌入式Apache Derby数据库中,也可以替换为其他客户端-服务器数据库,如MySQL。
- Hive不提供低延迟或实时查询。即使查询少量数据也可能需要几分钟时间。查询大量数据时,运行查询所需的时间相对较少。
- 为了支持模式和分区等功能,Hive将其元数据存储在关系数据库中。Hive打包了Derby(一个轻量级嵌入式SQL数据库),元存储可以轻松切换到其他SQL安装,如MySQL。


Hive架构 🏗️
上一节我们介绍了Hive的基本概念,本节中我们来看看Hive的架构。Hive架构的组件包括用户界面、元存储、HiveQL处理引擎、执行引擎以及HDFS或HBase数据存储。

以下是Hive架构的组件:
- 用户界面:Hive是一个数据仓库基础设施软件,可以在用户和HDFS之间创建交互。Hive支持的用户界面包括Web UI、Hive命令行和Hive HDInsight。在这些界面上,我们可以编写查询,这些查询将在后端与Hadoop集群交互。
- 元存储:Hive选择相应的数据库服务器来存储模式或元数据表、数据库、表中的列、它们的数据类型以及HDFS映射。元存储存储表、数据库等的元数据,还包括桶和分区信息。
- HiveQL处理引擎:HiveQL类似于SQL,用于查询元数据上的模式信息。它是MapReduce程序的替代方案之一,我们可以用HiveQL编写查询来处理MapReduce作业,而无需用Java编写冗长的MapReduce程序。
- 执行引擎:执行引擎处理查询并生成与MapReduce相同的结果。
- HDFS或HBase:HDFS是Hadoop分布式文件系统,可以将数据存储到Hadoop系统中。


Hive工作原理 ⚙️
了解了Hive的架构后,我们来看看它是如何工作的。Hive的组件包括接口驱动程序、执行引擎、编译器、元存储,以及后端的Hadoop集群(包括作业跟踪器、任务跟踪器、名称节点和数据节点)。
以下是Hive工作的详细步骤:
- 提交查询:Hive接口(如命令行或Web UI)将查询发送给驱动程序执行。
- 语法检查:驱动程序借助查询编译器来检查查询的语法和查询计划或需求。
- 请求元数据:编译器向元存储发送元数据请求。
- 获取元数据:元存储将元数据作为响应发送给编译器。
- 返回计划:编译器检查需求后,将计划重新发送给驱动程序。
- 执行计划:驱动程序将执行计划发送给执行引擎。
- 执行作业:执行引擎将作业发送给作业跟踪器(名称节点),作业跟踪器将此作业分配给任务跟踪器(数据节点)。查询在此执行MapReduce作业。在执行期间,执行引擎可以与元存储执行元数据操作。
- 接收结果:执行引擎从数据节点接收结果。
- 返回结果:执行引擎将这些结果发送给驱动程序。
- 显示结果:驱动程序将结果发送到Hive界面。

这就是Hive在后端运行的整个工作流程。

Hive数据类型 📊
上一节我们介绍了Hive的工作原理,本节中我们来看看Hive支持的数据类型。Hive的数据类型包括布尔型、整数型、字符串型和浮点型等基本类型。

以下是Hive支持的基本数据类型,这些类型与表中的列相关联:
- 整数类型:
TINYINT:1字节整数。SMALLINT:2字节整数。INT:4字节整数。BIGINT:8字节整数。
- 布尔类型:
BOOLEAN,包含TRUE或FALSE。 - 浮点类型:
FLOAT:单精度浮点数。DOUBLE:双精度浮点数。
- 字符串类型:
STRING,指定字符集中的字符序列。

这些类型在类型层次结构中组织,层次结构为:DOUBLE > FLOAT > BIGINT > INT > SMALLINT > TINYINT > STRING > BOOLEAN。
这个类型层次结构定义了类型在查询语言中如何隐式转换。允许从子类型到祖先类型的隐式转换。因此,当查询表达式期望类型1而数据是类型2时,只有当类型1是类型2在类型层次结构中的祖先时,类型2才能隐式转换为类型1。例如,类型层次结构允许字符串隐式转换为双精度浮点数。也可以使用CAST操作符进行显式类型转换。

Hive复杂数据类型 🧩
除了基本数据类型,Hive还支持复杂数据类型。复杂数据类型可以使用以下三种运算符从基本数据类型和其他复合类型构建。
以下是Hive支持的复杂数据类型:
- 结构体:
STRUCT数据类型。结构体中的每个元素可以使用点符号访问。例如,对于一个类型为STRUCT {a INT, b INT}的列c,字段a可以通过表达式c.a访问。 - 映射:
MAP包含键值对。元素使用元素名称和[‘key’]符号访问。例如,在一个包含从‘group’到gid映射的映射m中,gid值可以通过m[‘group’]访问。 - 数组:
ARRAY是可索引的列表。数组中的元素必须是相同类型。元素可以使用[n]符号访问,其中n是基于0的索引。例如,我们有一个数组a,元素为[‘a’, ‘b’, ‘c’]。那么a[0]将返回‘a’,a[1]返回‘b’,a[2]返回‘c’。
Hive数据模型 🗃️
最后,我们来了解Hive的数据模型。Hive数据模型包括数据库、表、分区和桶。
以下是Hive数据模型的组成部分:
- 数据库:数据库是命名空间,用于分隔表和其他数据单元,避免命名冲突。我们可以创建自己的数据库,并在这些特定数据库中创建表。
- 表:表是具有相同模式的同质数据单元。例如,一个页面浏览表,其中每行可以包含以下列:
timestamp(INT类型)、userid(BIGINT类型)、page_url(STRING类型)、referral_url(STRING类型)和ip(STRING类型)。我们可以根据需求创建任何表,并指定相应的数据类型。 - 分区:每个表可以有一个或多个分区键,用于确定数据的存储方式。分区除了是存储单元外,还允许用户高效识别满足特定条件的行。例如,一个类型为
STRING的日期分区和一个类型为STRING的国家分区。分区键的每个唯一值定义表的一个分区。例如,我们可以基于日期和国家创建分区。如果我们总是只分析美国在2009年12月23日的数据,那么查询或分析将仅在该特定分区上运行,而不会处理整个表的数据,从而减少分析数据的时间,提高查询效率。注意:分区列是虚拟列,它们不是数据本身的一部分,而是在加载时派生的。 - 桶:桶也称为簇。每个分区中的数据可以根据表中某列的哈希函数值再次划分为桶。例如,页面浏览表可以按
userid(页面浏览表中除分区列之外的列之一)进行分桶。桶也可以用于高效地对数据进行采样。表不一定需要分区或分桶,但这些抽象允许系统在查询处理期间修剪大量数据,从而加快查询执行速度。因此,创建分区和桶有助于Hive查询的性能调优。原本需要在整个表中搜索数据的查询,现在通过搜索特定分区和桶中的数据,将花费更少的时间。
总结 📝

本节课中我们一起学习了Apache Hive的基础知识。我们了解了Hive是一个构建在Hadoop之上的数据仓库工具,用于数据汇总、查询和分析。我们探讨了Hive的架构、工作原理、支持的数据类型(包括基本类型和复杂类型)以及其数据模型(包括数据库、表、分区和桶)。理解这些核心概念是使用Hive进行大数据分析的第一步。
002:Hive中的数据库与表命令 🗄️
在本节课中,我们将学习如何在Hive中操作数据库和表。主要内容包括数据库命令和表命令。数据库命令用于创建、查看、描述、修改和删除数据库。表命令则用于创建、查看、描述、修改和删除表。我们还将学习管理表和外部表的区别。
数据库命令
首先,我们来学习数据库相关的命令。这些命令允许我们创建、查看和管理Hive中的数据库。
创建数据库

创建数据库的命令是 CREATE DATABASE。其基本语法如下:
CREATE DATABASE [IF NOT EXISTS] database_name
[COMMENT 'database_comment']
[LOCATION 'hdfs_path']
[WITH DBPROPERTIES ('property_name'='property_value', ...)];

IF NOT EXISTS:这是一个可选关键字。如果指定,当存在同名数据库时,Hive不会报错,也不会创建新数据库。如果不存在同名数据库,则会创建它。LOCATION:这是一个可选关键字。用于指定数据库在HDFS上的存储路径。如果不指定,Hive会使用默认的仓库目录。COMMENT和WITH DBPROPERTIES:用于为数据库添加注释和自定义属性。
例如,创建一个名为 user1 的数据库:
CREATE DATABASE user1;
创建一个带有注释和自定义属性的数据库 user2:
CREATE DATABASE IF NOT EXISTS user2
COMMENT ‘这是一个测试数据库’
WITH DBPROPERTIES (‘creator’=‘admin’, ‘date’=‘2023-10-27’);

查看数据库
创建数据库后,我们可以使用 SHOW DATABASES 命令来查看所有数据库。
SHOW DATABASES;

如果想查看名称符合特定模式的数据库(例如,所有以 ‘user’ 开头的数据库),可以使用 LIKE 关键字:
SHOW DATABASES LIKE ‘user*’;
描述数据库

要查看某个数据库的详细信息,如位置、所有者、注释和属性,可以使用 DESCRIBE DATABASE 命令。
DESCRIBE DATABASE database_name;
例如,查看数据库 user2 的信息:
DESCRIBE DATABASE user2;
如果想查看更详细的信息,包括通过 WITH DBPROPERTIES 设置的属性,可以使用 DESCRIBE DATABASE EXTENDED 命令。

DESCRIBE DATABASE EXTENDED user2;
修改数据库

ALTER DATABASE 命令用于修改数据库的属性。需要注意的是,Hive目前只允许修改 DBPROPERTIES 中的值,不能删除或重设所有属性,也不能修改数据库名或位置。
ALTER DATABASE database_name SET DBPROPERTIES (‘property_name’=‘property_value’);
例如,为数据库 user1 添加一个 ‘edited_by’ 属性:
ALTER DATABASE user1 SET DBPROPERTIES (‘edited_by’=‘robot’);

删除数据库
删除数据库使用 DROP DATABASE 命令。
DROP DATABASE [IF EXISTS] database_name [RESTRICT | CASCADE];

IF EXISTS:可选。防止删除不存在的数据库时报错。CASCADE:非常重要。默认行为是RESTRICT。如果数据库不为空(包含表),直接使用DROP DATABASE会失败。必须加上CASCADE关键字,Hive才会先删除库中的所有表,然后再删除数据库本身。
例如,删除一个空数据库 user1:
DROP DATABASE IF EXISTS user1;
删除一个非空数据库 user_db:
DROP DATABASE user_db CASCADE;
上一节我们详细介绍了Hive中数据库的各类操作命令。接下来,我们将目光转向表,学习如何在数据库中创建和管理表。
表命令
在Hive中,表是存储数据的主要结构。我们可以创建结构复杂的表来适应不同的数据模型。

创建表

创建表的命令是 CREATE TABLE,其语法比创建数据库更为丰富,允许定义列、数据类型、注释、表属性及存储位置。
CREATE [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name
(
col_name data_type [COMMENT col_comment],
col_name data_type [COMMENT col_comment],
...
)
[COMMENT table_comment]
[ROW FORMAT row_format]
[STORED AS file_format]
[LOCATION ‘hdfs_path’]
[TBLPROPERTIES (‘property_name’=‘property_value’, ...)];

Hive支持复杂的数据类型,例如:
ARRAY<data_type>:数组。MAP<primitive_type, data_type>:键值对映射。STRUCT<col_name : data_type, ...>:结构体,可以包含多个字段。
让我们看一个创建员工表的例子,它使用了多种数据类型:
CREATE TABLE IF NOT EXISTS financials.employee (
name STRING COMMENT ‘员工姓名’,
salary FLOAT COMMENT ‘薪水’,
subordinates ARRAY<STRING> COMMENT ‘下属名单’,
deductions MAP<STRING, FLOAT> COMMENT ‘扣款项(名称,百分比)’,
address STRUCT<street:STRING, city:STRING, state:STRING, zip:INT> COMMENT ‘家庭住址’
)
COMMENT ‘员工信息表’
TBLPROPERTIES (‘creator’=‘admin’, ‘created_date’=‘2023-10-27’);
此外,还可以基于现有表的结构创建一个新表(只复制结构,不复制数据):
CREATE TABLE new_table_name LIKE existing_table_name;

查看表
在某个数据库内,可以使用 SHOW TABLES 查看所有表。
USE database_name; -- 切换到目标数据库
SHOW TABLES;
或者直接指定数据库查看:
SHOW TABLES IN financials;
同样,可以使用 LIKE 进行模式过滤:
SHOW TABLES LIKE ‘emp*’;

描述表


DESCRIBE 命令用于查看表的列信息。
DESCRIBE [EXTENDED|FORMATTED] table_name;

DESCRIBE table_name:显示列名、数据类型和注释。DESCRIBE EXTENDED table_name或DESCRIBE FORMATTED table_name:显示详细信息,包括表属性、存储信息、创建时间、所有者等。

修改表

ALTER TABLE 命令可以修改现有表的结构,例如重命名表、添加列、修改列名或类型。
- 重命名表:
ALTER TABLE old_table_name RENAME TO new_table_name;

- 添加列:
ALTER TABLE table_name ADD COLUMNS (new_col_name data_type [COMMENT col_comment], ...);

-
修改列(可以修改列名、数据类型、注释或位置):
ALTER TABLE table_name CHANGE COLUMN old_col_name new_col_name new_data_type [COMMENT new_comment] [FIRST|AFTER column_name]; -
替换所有列:
ALTER TABLE table_name REPLACE COLUMNS (col_name data_type [COMMENT col_comment], ...);
删除表

删除表使用 DROP TABLE 命令。对于管理表,删除操作会同时删除元数据和HDFS上的数据。
DROP TABLE [IF EXISTS] table_name;

在学习了表的创建、查看、修改和删除等基本操作后,我们需要理解Hive中两种重要的表类型:管理表和外部表。它们的区别对于数据生命周期管理至关重要。
管理表 vs 外部表

管理表 (Managed Table)
到目前为止我们创建的表都是管理表,也称为内部表。
- 特点:Hive完全管理这种表的数据生命周期。
- 存储:数据默认存储在Hive配置的仓库目录下(例如:
/user/hive/warehouse/db_name.db/table_name)。 - 删除后果:使用
DROP TABLE时,Hive会同时删除元数据(表结构)和HDFS上的实际数据。 - 适用场景:适用于Hive内部临时或中间数据,数据完全由Hive控制。
外部表 (External Table)
外部表提供了一种将Hive元数据与数据存储解耦的方式。
- 创建:在
CREATE TABLE语句前加上EXTERNAL关键字,并通常通过LOCATION指定一个已存在数据的HDFS路径。CREATE EXTERNAL TABLE IF NOT EXISTS external_employee ( ... ) LOCATION ‘/user/admin/employee_data’; - 特点:Hive并不拥有该位置的数据。它只是创建了一个指向该位置的元数据映射。
- 删除后果:使用
DROP TABLE时,Hive只删除元数据,而不会删除HDFS路径下的实际数据。 - 适用场景:
- 数据需要被多个工具(如Pig、Spark、Impala)共享。
- 数据由其他流程生成或管理,Hive只用于查询。
- 希望避免误删除操作导致数据丢失。
选择管理表还是外部表,取决于数据的所有权、共享需求以及对数据安全性的要求。
总结
本节课我们一起学习了Hive中关于数据库和表的核心操作。
- 数据库命令:我们掌握了如何使用
CREATE DATABASE、SHOW DATABASES、DESCRIBE DATABASE、ALTER DATABASE和DROP DATABASE来创建、查看、描述、修改和删除数据库,并理解了CASCADE关键字在删除非空数据库时的必要性。 - 表命令:我们学习了如何使用
CREATE TABLE创建包含复杂数据类型的表,以及如何使用SHOW TABLES、DESCRIBE、ALTER TABLE和DROP TABLE来查看、描述、修改和删除表。 - 表类型:我们重点区分了管理表和外部表。管理表的数据由Hive全权管理,删除表会删除数据;而外部表的数据存储在Hive之外,删除表仅删除元数据,不影响底层数据,更适合多框架共享数据的场景。

理解这些基本命令和概念,是使用Hive进行数据定义和管理的坚实基础。
003:外部表、分区与分桶

在本节课中,我们将学习Hive中外部表与托管表的核心区别,并深入探讨如何通过分区和分桶技术来优化数据组织与查询性能。

外部表与托管表的区别
托管表和外部表的主要区别在于,当删除托管表时,会同时删除其数据和元数据。而当删除外部表时,仅删除元数据。存储在指定位置的数据本身是完整的,在执行 DROP TABLE 命令时不会被删除。
我们也可以在终端上运行之前学过的查询。





具体操作是,在终端输入 hive 并回车,即可进入Hive环境。在这里,我们可以运行所有在Cloudera环境的Hue浏览器中学过的查询。这些查询同样可以在终端运行。例如,我运行了查询 USE user1 来使用 user1 数据库,还使用了 SHOW TABLES 来显示 user1 数据库中的表。由于 user1 数据库没有表,因此没有输出任何内容。使用 USE financials 切换到 financials 数据库,它包含一些表,如 employ1,所以输出了 employ1。简而言之,我们可以在终端运行所有在Hue浏览器中运行过的查询。


今天的课程到此结束。非常感谢。

大家好,今天我们将讨论分区和分桶。我们将学习托管表中的分区、外部表中的分区、分区命令(包括描述、添加、重命名和删除分区)、分桶,以及一个基于电信数据的案例研究。
首先,我们来看托管表中的分区。我们可以在内部表(即托管表)和外部表中创建或添加分区。本节将学习如何创建表以及如何在表中添加分区,以优化查询性能。

以下是一个简单的 employees 表示例,它包含 name、salary 和 age 列,并按 country 和 state 列进行分区。让我们运行它。


我们将在Hive查询编辑器中运行查询。创建表 employees,包含 name(字符串类型)、salary(浮点类型)和 age(小整数类型)。现在,我们将添加 PARTITIONED BY 子句,并指定分区列及其数据类型。这里按 country(字符串类型)和 state(字符串类型)分区。然后,我们设置行格式为以制表符分隔的字段。我们可以根据数据文件的存储格式,指定字段以制表符、空格或其他任何字符分隔。只有这样,我们才能将数据从文件加载到特定的表中。运行这个查询。成功。刷新后,可以看到 employees 表已创建。我们添加了 name、salary 和 age 的数据类型,但没有为分区列指定数据类型,而是将 country 和 state 添加为分区列。这意味着分区已经创建。我们也可以检查分区是否已创建。表的默认目录创建在 /user/hive/warehouse 下。是的,employees 表目录已经创建。我们可以看到,目前还没有为所有分区创建子目录。在 employees 目录下,本应为分区创建 country 和 state 子目录,但它们尚未出现。这是因为我们还没有加载任何数据到表中,仅仅创建了表的结构。当我们加载数据,并指定基于特定州和特定国家创建分区时,子目录才会被创建。


我们将在下一节学习这一点。现在,分区表改变了Hive组织数据存储的方式。如果我们在 my_db 数据库中创建表,将会为该表创建 employees 目录。正如我们在Hive仓库中看到的,employees 表的目录已经创建。它还将创建反映分区结构的子目录。


这里给出了一个 employees 表示例,基于国家 CA 和州 AB 进行分区。当我们添加数据时,就会创建这类分区。分区数据最主要的原因是为了实现更快的查询,从而提高查询性能。但是,只有当分区方案反映了常见的范围过滤条件时,分区才有可能。

接下来看外部表中的分区。将分区与外部表结合使用,可以在优化查询性能的同时,提供与其他工具共享数据的方式。众所周知,创建外部表是为了也能与其他工具共享数据。因此,这是一举两得的事情:我们创建的外部表可以与其他工具共享,同时也在优化表的查询性能。我们在目录结构上也拥有更大的灵活性。在之前学习创建非分区外部表时,需要 LOCATION 子句,但在外部分区表中则不需要。我们不需要指定任何位置,而是使用 ALTER TABLE 语句来分别添加每个分区。我们将在接下来的示例中学习这一点。


这里有一个简单的示例。创建外部表 log_messages,包含 server、process_id 和 message 列,并按 year、month 和 day 进行分区。同样,我们设置了行格式,字段以制表符分隔。正如在上面的幻灯片中读到的,我们将通过 ALTER TABLE 命令添加分区。这个命令是:ALTER TABLE log_messages ADD PARTITION,我们为分区指定静态值,例如 year=2012、month=1、day=2。除此之外,我们还要指定保存该分区数据的位置。让我们创建一个外部表。


创建外部表 log_message(如果不存在)。字段包括 server(字符串类型)、process_id(整数类型)和 message(字符串类型)。然后按 year、month 和 day 分区。设置行格式,字段以制表符分隔。我们正在创建一个外部表,但没有指定任何位置。是的,log_messages 表已经创建。可以看到,server、process_id、message 这些列都已创建,还有 year、month、day。我们将在下一节学习如何向这些分区添加数据,届时将学习如何向表中加载数据。

现在,让我们学习分区命令。如果我们想查看分区是否已创建,可以使用 SHOW PARTITIONS 命令,其语法是 SHOW PARTITIONS table_name。



让我们检查一下分区是否已创建。SHOW PARTITIONS employees。可以看到,它没有显示任何分区,因为我们还没有向分区添加数据。尝试 SHOW PARTITIONS log_messages。这里,我们看到表未找到,因为它是外部表,正确的表名是 log_message。同样,我们看不到任何分区,因为我们还没有在表中创建任何分区或加载数据。我们还有另一个查询:DESCRIBE EXTENDED log_messages。




这里定义了详细信息。通过 DESCRIBE EXTENDED 命令,我们可以看到列名 name、salary、age、country 和 state,但分区信息 country 和 state 在这里以详细格式给出。也检查一下 log_messages。同样,你可以看到所有列,分区列是 year、month 和 day。


让我们学习另一个命令。关于我们创建的 log_message 外部表,这里缺少一些信息。它没有显示分区创建的位置。现在,它也不会显示任何位置,因为我们还没有添加数据。我们可以使用 DESCRIBE EXTENDED log_messages PARTITION 并指定通过 ALTER TABLE 命令添加的特定分区,来检查该分区的位置。


如前所述,我们可以通过 ALTER TABLE 命令添加分区,并为我们选择的所有分区列指定静态值,同时指定位置。我们将在下一节学习所有这些内容,届时我们将向表中添加数据,然后运行查询。



我们也可以重命名分区:ALTER TABLE table_name PARTITION partition_spec RENAME TO PARTITION partition_spec。例如,ALTER TABLE log PARTITION (year=2012, month=1, day=2) RENAME TO PARTITION (year=2013, month=1, day=2)。还可以使用 DROP 命令删除特定分区:ALTER TABLE employees DROP IF EXISTS PARTITION,然后指定特定分区。现在,让我们学习分桶。

当分区数量有限且分区划分均匀时,分区能产生有效的结果。但这并非在所有情况下都可能。因此,使用分桶。分桶是一种将表数据集分解为更易管理部分的技术。分桶的作用是创建桶,我们在创建表时指定桶的数量,数据被放入这些桶中。实际的数据段被放入这些桶中。分桶的概念基于分桶列上的哈希函数。我们使用 CLUSTERED BY 子句将表划分为桶。分桶表将创建数据量几乎相等的桶。以下是一个分桶示例。


创建表 bucketing_example,数据列是 name 和 id,它按 location 分区,并使用 CLUSTERED BY 进行分桶,我们基于 id 将其分为50个桶。为了强制分桶并将数据加载到桶中,我们应该设置 hive.enforce.bucketing=true。让我们创建一个表。


创建表 bucketing_example。包含 name 和 id 列,按 location(字符串类型)分区。我们使用了 CLUSTERED BY id INTO 50 BUCKETS。根据数据量,我们可以设置桶的大小,从而选择合适的桶数量。运行这个查询。是的,我们已经创建了 bucketing_example 表。字段包括 name、id 和 location。


现在,让我们讨论案例研究。我们将学习电信数据的案例研究。在电信数据中,我们有客户号码。根据这些号码,电信行业收集了一些数据。我选取了几个表和几列,我们将学习如何在数据上实现Hive。首先是表 directory_number。数据字段包括:DN_ID(唯一ID)、EL_CODE(EL代码)、DNUM(客户号码)、STATUS(号码状态)、DN_STATUS_MOD_DATE(号码状态修改日期)、DEALER_ID(经销商ID,表示号码是预付费还是后付费)、DN_END_DATE(号码的激活结束日期)以及 DN_MOD_DATE(号码的最后修改日期)。在这个表中,我们将基于 DEALER_ID 和 EL_CODE 创建分区,并基于 STATUS 创建桶。让我们创建一个表。本节我们将讨论所有表,并学习如何通过表创建数据结构,即如何创建表并实现我们上面学到的分区和分桶。

还有另一个表 contract_services_cap。数据字段包括:CO_ID(唯一的客户ID)、SN_CODE(在号码上实现的服务)、SEQUENCE_NUMBER(客户号码服务变更的序列号)、DEALER_ID(又是一个唯一号码)、CS_STATUS(号码状态)、CS_ACTIVE_DATE(号码激活日期)、CS_DEACTIVE_DATE(号码停用日期)以及 CS_REQUEST(对号码进行任何更改的请求号)。这里,我们将基于服务代码 SN_CODE 对表进行分区。


现在我们还有表 q_services。

总结


在本节课中,我们一起学习了Hive中外部表与托管表的关键区别,理解了删除操作对数据的影响。然后,我们深入探讨了分区技术,包括如何在托管表和外部表中创建分区,以及分区如何通过优化数据存储结构来提升查询性能。我们还介绍了分桶技术,它通过哈希函数将数据均匀分布到多个桶中,适用于分区不均衡的场景。最后,我们通过一个电信数据的案例研究,初步了解了如何在实际数据表上应用分区和分桶策略来设计高效的数据结构。
004:Hive中的表控制服务 🗂️
在本节课中,我们将学习如何在Hive中创建和管理用于电信数据分析的核心表。我们将详细探讨三个关键表的结构,并演示如何使用HiveQL创建它们,包括定义列、添加注释以及设置分区。
表结构概述

我们主要处理三个表:directory_number、control_services 和 contract_all。每个表都包含与客户、服务和套餐计划相关的特定字段。为了提高查询效率,我们将根据业务逻辑对表进行分区。
1. 控制服务表 (control_services)
此表存储客户服务变更的详细信息。数据字段包括:
- CoID:唯一的客户ID。
- TM Code:套餐计划代码,代表分配给客户的特定套餐。
- S Code:在套餐内应用于客户号码的服务代码。
- Sequence Number:客户服务变更的序列号。
- CS State Change:状态号码的变更。
- C S Date Built:账单日期。
- CS Request:状态变更的请求号。
- CS End Date:号码的结束日期。
为了提高查询性能,我们将根据 TM Code 和 S Code 对该表进行分区。这样,当我们在特定套餐或服务中搜索号码时,效率会更高。

2. 合同总表 (contract_all)
此表记录客户合同的核心信息。字段包括:
- CoID:唯一的客户代码(客户ID)。
- CoAct:客户ID的激活日期。
- CoID End Date:客户ID的结束日期。
- CoMod:客户ID的最后修改日期。
- User Last Modification:进行最后修改的用户。电信公司会跟踪所有对客户号码进行任何操作的人员。
- TM Code:套餐计划代码。
- T Code Date:套餐分配日期。
我们将根据 User Last Modification 和 T Code 对该表进行分区。


3. 客户总表 (customer_all)

此表包含客户级别的详细信息。字段包括:
- Customer ID:客户ID。
- Cast Code:客户代码(同样是唯一代码)。系统会根据不同层级(如CoID和号码)生成多种唯一代码。
- CS Type:状态类型。
- CS Activated:号码激活日期。
- TM Code:套餐计划。
- PRG Code:分配给客户号码的某种代码。
- Cs Cur Ball:客户的当前余额。
- Bill Cycle:客户的账单周期日期。每个客户在月中都有特定的账单周期日期。
- Birth Date:出生日期。
- Previous Balance:任何前期余额。
- User Last Mode:最后进行修改的用户。
- C Mode Date:最后的修改日期。

我们将根据 TM Code 和 PRG Code 对该表进行分区。
实践:在Hive中创建表
现在,让我们实际操作,看看如何在Hive中创建这些表。
创建目录号码表 (directory_number)


首先,如果已存在同名表,我们先删除它。


DROP TABLE IF EXISTS directory_number;
接下来,创建新表。我们将定义列及其数据类型,并为列和表本身添加注释,这有助于理解表结构。我们还将根据 Dealer ID 和 HL Code 进行分区,并根据 DN Status 进行分桶。
CREATE TABLE IF NOT EXISTS directory_number (
DNID INT COMMENT ‘号码ID’,
DNNum BIGINT COMMENT ‘客户号码’,
DNStatus STRING COMMENT ‘号码状态’,
DNStatusModDate STRING COMMENT ‘号码状态修改日期’,
DNDate STRING COMMENT ‘号码激活日期’,
DNModDate STRING COMMENT ‘号码修改日期’
)
COMMENT ‘这是目录号码表’
PARTITIONED BY (DealerId INT, HLCode SMALLINT)
CLUSTERED BY (DNStatus) INTO 100 BUCKETS
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ‘,’
LINES TERMINATED BY ‘\n’
STORED AS TEXTFILE;
执行成功后,表结构将包含定义的列以及分区列 DealerId 和 HLCode。


创建控制服务表 (control_services)
上一节我们创建了目录表,本节我们来创建服务控制表。以下是创建 control_services 表的步骤。
首先,定义表的列结构:
CREATE TABLE IF NOT EXISTS control_services (
CoID BIGINT COMMENT ‘唯一客户ID’,
SequenceNumber INT COMMENT ‘服务变更的序列号’,
DNID BIGINT COMMENT ‘唯一号码ID’,
CSStatus STRING COMMENT ‘号码状态’,
ActiveDate STRING COMMENT ‘激活日期’,
CSEndDate STRING COMMENT ‘结束日期’,
CSRequest BIGINT COMMENT ‘状态变更的请求号’
)
COMMENT ‘这是控制服务表’
PARTITIONED BY (SNCode INT)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ‘,’
LINES TERMINATED BY ‘\n’
STORED AS TEXTFILE;
请注意,我们最初创建时遗漏了 SNCode 分区。如果已创建不完整的表,需要先删除再重建。
创建合同服务表 (con_services)
现在,让我们创建第三个表 con_services。希望您能理解这些电信数据表的结构。我们正在创建五个表的Hive结构,以便后续进行数据分析。
以下是创建该表的SQL语句:
CREATE TABLE IF NOT EXISTS con_services (
CoID BIGINT COMMENT ‘唯一客户ID’,
SequenceNumber INT COMMENT ‘服务变更序列’,
CSStateChange STRING COMMENT ‘号码状态变更’,
CSDateBuilt DATE COMMENT ‘账单日期’,
CSRequest BIGINT COMMENT ‘状态变更号’,
CSEndDate STRING COMMENT ‘号码结束日期’
)
COMMENT ‘合同服务表’
PARTITIONED BY (TmCode INT, SCode INT)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ‘,’
LINES TERMINATED BY ‘\n’
STORED AS TEXTFILE;
执行后,con_services 表即创建成功,包含定义的列以及分区列 TmCode 和 SCode。


总结
本节课中,我们一起学习了电信数据分析中三个核心Hive表的结构与创建方法。我们详细介绍了 control_services、contract_all 和 customer_all 表的字段含义,并实践了如何使用HiveQL语句创建这些表,重点包括:
- 定义列名、数据类型和添加注释。
- 根据业务查询模式(如按套餐、服务或用户)设置分区键,以优化查询性能。
- 指定数据存储格式和分隔符。

掌握这些表的创建是后续进行数据加载、查询和分析的基础。
005:创建合同全表与客户角色表 📊

在本节课中,我们将学习如何在Hive中创建两个核心数据表:contract_all(合同全表)和customer_role(客户角色表)。我们将详细讲解每个表的列定义、数据类型、注释以及分区策略。
创建合同全表 (contract_all)
首先,我们来创建 contract_all 表。该表用于存储所有合同的核心信息。


以下是创建该表的完整HiveQL语句:
CREATE TABLE IF NOT EXISTS contract_all (
Kaiude BIGINT COMMENT '唯一的客户报价标识',
Customer_ID BIGINT COMMENT '唯一的客户标识',
Coactivated STRING COMMENT '合同激活日期',
Dead STRING COMMENT '合同失效日期',
Mad STRING COMMENT '合同最后修改日期',
team_code_date STRING COMMENT '团队代码变更日期'
)
COMMENT '合同总表'
PARTITIONED BY (Co_user_last_mo STRING, TM_code INT)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
LINES TERMINATED BY '\n'
STORED AS TEXTFILE;

核心概念解析:
PARTITIONED BY: 此语句用于对表进行分区。分区可以将表数据按指定列(如Co_user_last_mo和TM_code)的值物理分割到不同的目录中,能显著提升针对分区列的查询效率。ROW FORMAT: 定义了数据文件中行和字段的格式。这里指定字段由制表符(\t)分隔,行由换行符(\n)终止。STORED AS TEXTFILE: 指定表的数据以纯文本格式存储。

创建客户角色表 (customer_role)
上一节我们创建了合同总表,本节中我们来看看如何创建 customer_role 表。此表用于存储客户级别的详细信息。
以下是创建该表的HiveQL语句:
CREATE TABLE IF NOT EXISTS customer_role (
Customer_ID BIGINT COMMENT '唯一的客户标识',
Cast_code FLOAT COMMENT '客户等级代码',
Css_activated STRING COMMENT '客户代码激活日期',
Sius_D_active STRING COMMENT '客户代码失效日期',
Ss_girl_bell FLOAT COMMENT '客户当前余额',
Bill_Cy INT COMMENT '客户账单周期编号',
birth_date STRING COMMENT '客户出生日期',
Previous_balance FLOAT COMMENT '客户先前余额',
User_last_mode STRING COMMENT '用户最后模式',
C_is_more_date STRING COMMENT '最后修改日期'
)
COMMENT '客户角色信息表'
PARTITIONED BY (Tm_code INT, PRG_code INT)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
LINES TERMINATED BY '\n'
STORED AS TEXTFILE;
表结构说明:
以下是该表各列及其含义的详细列表:
Customer_ID: 客户唯一标识。Cast_code: 表示客户等级或类型的代码。Css_activated: 记录客户代码激活的日期。Sius_D_active: 记录客户代码失效的日期。Ss_girl_bell: 显示客户账户的当前余额。Bill_Cy: 指示客户的账单周期编号。birth_date: 客户的出生日期。Previous_balance: 客户先前的账户余额。User_last_mode: 用户最后使用的模式或状态。C_is_more_date: 该条记录的最后修改日期。
总结与回顾 ✅

本节课中,我们一起学习了在Hive中创建数据表的详细过程。我们成功创建了两个表:contract_all 和 customer_role。关键点包括:
- 使用
CREATE TABLE语句定义表结构,并为每个列指定了合适的数据类型(如BIGINT,STRING,FLOAT,INT)和描述性注释。 - 理解了 分区(
PARTITIONED BY) 的概念及其在优化查询性能中的作用。 - 掌握了如何通过
ROW FORMAT和STORED AS子句指定数据的存储格式。


详细了解每个列的含义对于后续在表上高效、准确地执行查询至关重要。在下一节课中,我们将讨论如何将数据加载到这些已创建的表中。
006:社交媒体产业导论 🎯
在本节课中,我们将学习如何分析社交媒体产业中的书签网站数据。我们将使用Hadoop生态系统中的工具(如Sqoop、MapReduce、Pig和Hive)来处理和分析半结构化的XML数据,以评估书籍在市场上的表现。
场景与数据介绍
上一节我们介绍了课程的整体目标,本节中我们来看看具体的业务场景和数据格式。
我们面临一个业务场景:一个第三方公司收集了书签网站的数据,并将其存储在关系型数据库管理系统(RDBMS)中。这些数据包含书籍ID、名称、评论、链接、评论内容等信息。
我们的任务是分析这些书签网站的数据。通过分析用户对书籍的评论(如“优秀”、“一般”、“差”),我们可以评估某本书在市场上的表现,例如销量、用户喜好程度以及正面/负面评论的比例。
输入数据最初通过Sqoop从RDBMS导入到Hadoop分布式文件系统(HDFS)。但为了本课程演示,我们假设数据已被某个第三方ETL工具或Java程序转换为XML格式。因此,我们的起点是一个XML文件。
以下是输入XML文件的结构示例:
<data>
<book>
<bookid>1001</bookid>
<bookname>Five Point Someone</bookname>
<category>Drama</category>
<author>Chetan Bhagat</author>
<location>America</location>
<reviewcomment>Awesome</reviewcomment>
</book>
<book>
<bookid>1002</bookid>
<bookname>War and Peace</bookname>
<category>Action</category>
<author>Leo Tolstoy</author>
<location>Africa</location>
<reviewcomment>Average</reviewcomment>
</book>
</data>
数据包含多个字段,如书籍ID、名称、类别、作者、用户所在地和评论。我们的目标是处理这个XML文件,并根据评论内容(如“Awesome”视为正面,“Bad”视为负面)进行情感分析。
技术栈概览
在深入处理数据之前,我们先简要了解一下本课程将要用到的核心大数据工具。
- Sqoop:这是一个在HDFS和关系型数据库(如MySQL、Oracle)之间传输数据的工具。它就像一个桥梁。在本课程中,我们将首先使用Sqoop将数据从RDBMS导入HDFS。
- MapReduce:这是Hadoop的核心编程模型,用于大规模数据集的并行处理。一个MapReduce程序通常包含三个类:
Mapper类:读取并处理输入数据。Reducer类:对Mapper的输出进行聚合(如求和、计数)。Driver类:主类,用于配置和提交作业。
- Pig:这是一个基于MapReduce的高级数据流语言。它提供了更简单的脚本来表达复杂的数据转换,而无需编写冗长的Java代码。Pig适用于数据清洗和转换。
- Hive:这是一个构建在Hadoop之上的数据仓库工具,它提供了类似SQL的查询语言(HiveQL)。Hive适用于数据分析和报告生成,但不适合频繁的更新/删除操作。
简单来说,Sqoop用于数据迁移,MapReduce和Pig用于数据处理,而Hive用于数据分析。
第一步:使用Sqoop导入数据
了解了工具后,我们开始第一步:将数据从数据库导入HDFS。虽然我们的最终输入是XML文件,但这一步演示了数据如何从传统系统进入Hadoop环境。
我们将使用一个在线的MySQL数据库(db4free.net)进行演示。您也可以使用本地安装的数据库。
以下是使用Sqoop从MySQL数据库导入employee表数据到HDFS的步骤:

- 准备JDBC驱动:将MySQL的JDBC驱动JAR包(例如
mysql-connector-java-8.0.23.jar)复制到Sqoop的lib目录下。 - 执行Sqoop导入命令:在终端中运行以下命令。
sqoop import \
--connect jdbc:mysql://db4free.net/hadoop2323 \
--username hadoop2323 \
--password hadoop2323 \
--table employee \
--target-dir /user/scoopout1 \
-m 1
命令参数解释:
--connect:指定数据库连接字符串。--username和--password:数据库登录凭据。--table:要导入的表名。--target-dir:HDFS上的目标目录,用于存储导入的数据。-m 1:使用1个Map任务来执行导入。


- 验证结果:命令执行成功后,您可以在HDFS的
/user/scoopout1目录下找到导入的数据文件,文件内容为纯文本格式。

重要提示:在本课程的实际案例中,我们假设这个从数据库导出的纯文本文件,随后被一个第三方流程转换成了我们即将处理的XML格式。因此,Sqoop步骤是数据流水线的起点。



课程总结与下一步
本节课中我们一起学习了社交媒体数据分析项目的背景、目标数据(XML格式)以及我们将要使用的核心大数据工具(Sqoop, MapReduce, Pig, Hive)。
我们特别演示了如何使用Sqoop将数据从关系型数据库(MySQL)迁移到HDFS中,这是大数据处理流程中常见的初始步骤。


在接下来的课程中,我们将直接面对核心挑战:处理半结构化的XML输入文件。我们将编写一个自定义的MapReduce程序,将这个XML文件解析并转换为结构化的纯文本格式,为后续使用Pig或Hive进行书籍评论分析做好准备。
007:执行MapReduce程序处理XML文件 📊
在本节课中,我们将学习如何将编写好的MapReduce程序打包成JAR文件,并在Hadoop集群上执行,以处理XML格式的数据。我们将从导出JAR文件开始,逐步完成在Hadoop环境中的部署、执行和结果验证。

概述
在之前的课程中,我们编写了用于处理XML数据的自定义输入格式和MapReduce程序。本节我们将实际操作,完成以下步骤:
- 将项目导出为JAR文件。
- 将JAR文件和输入数据上传到Hadoop集群。
- 在Hadoop上执行MapReduce作业。
- 检查并获取输出结果。


导出JAR文件
上一节我们介绍了自定义输入格式的代码,本节中我们来看看如何将其打包以便在Hadoop上运行。
首先,需要将Eclipse中的项目导出为可执行的JAR文件。


以下是导出JAR文件的步骤:
- 在Eclipse中,右键点击项目名称。
- 选择
Export选项。 - 在导出窗口中,选择
Java->JAR file,然后点击Next。 - 为JAR文件命名,例如
social_media_xml.jar。 - 点击
Finish完成导出。


导出后,JAR文件将保存在本地机器上。



准备Hadoop环境


在运行程序之前,必须确保Hadoop集群的所有必要服务都在运行。
以下是需要检查的Hadoop服务:
- ResourceManager: 资源管理器。
- NodeManager: 节点管理器。
- NameNode: 名称节点。
- DataNode: 数据节点。
可以通过在Hadoop主节点上执行 jps 命令来验证这些服务是否已启动。只有所有服务都正常运行,才能提交MapReduce作业。
上传文件到Hadoop
接下来,需要将导出的JAR文件和输入数据文件上传到Hadoop集群的根目录。
可以使用 scp 命令或图形化工具(如WinSCP)进行文件传输。确保JAR文件(如 social_media_xml.jar)和XML输入文件(如 books_data.xml)都位于Hadoop用户的主目录下。
执行MapReduce作业
文件准备就绪后,就可以在Hadoop上执行MapReduce程序了。
使用以下命令格式提交作业:
hadoop jar <jar文件名> <主类名> <输入路径> <输出路径>
在我们的例子中,命令如下:
hadoop jar social_media_xml.jar /user/hadoop/in_project/books_data.xml /user/hadoop/out_xml
执行此命令后,Hadoop的ResourceManager会为作业分配一个唯一的Job ID。控制台将显示Map和Reduce任务的执行进度。由于我们的程序只包含Mapper,因此只会看到Map任务完成。
验证输出结果
作业成功完成后,可以在指定的输出目录中查看结果。
使用以下命令检查输出目录:
hdfs dfs -ls /user/hadoop/out_xml
要查看输出文件的内容,可以使用 cat 或 get 命令将其下载到本地查看:
hdfs dfs -get /user/hadoop/out_xml/part-m-00000 ./output.txt
cat output.txt
输出文件的内容将是转换后的扁平格式数据,各字段由星号(*)分隔。
后续数据处理
我们得到的输出文件是一个扁平化的文本文件,可以作为另一个MapReduce作业的输入,以进行进一步分析,例如统计每本书的好评、中评和差评数量。

在下一个程序中,我们将:
- 读取上一作业的输出文件。
- 在Mapper中解析每条记录,分离出“评论”字段。
- 根据预定义的规则(如“awesome”为好评,“bad”为差评)对评论进行分类计数。
- 输出每本书的ID、名称、类别以及对应的好评、差评、中评数量。
第二个作业的执行方式与第一个类似,只需指定新的输入路径和输出路径即可。

总结



本节课中我们一起学习了MapReduce程序从打包到执行的完整流程。我们掌握了如何将Java项目导出为JAR文件,如何检查Hadoop服务状态,如何上传文件到HDFS,以及如何使用 hadoop jar 命令提交作业。最后,我们还查看了作业的输出结果,并了解了如何将本次作业的输出作为下一次数据分析的输入。这个过程是进行大规模数据处理的基础。
008:按地点分析书籍 📚
在本节课中,我们将学习如何使用MapReduce程序,根据用户的地理位置信息来分析图书数据。我们将统计每个国家或地区的用户数量。
上一节我们基于评论内容分析了数据。本节中,我们来看看如何基于地理位置进行分析。
概述
假设一个图书网站或出版商希望分析其用户分布。他们需要知道在哪些国家或地区拥有读者。我们的输入数据中包含用户的地理位置信息(例如:美国、德国、印度)。本教程的目标是编写一个MapReduce程序,统计每个地理位置出现的次数,从而得出用户分布情况。
程序逻辑
以下是实现该功能的核心逻辑:
-
Mapper阶段:读取输入文件的每一行,提取出地理位置字段。由于一个用户可能关联多个地点(用逗号分隔),我们需要将其拆分。然后,将每个独立的地点作为键(Key)输出,值(Value)固定为
1。- 公式/逻辑:对于输入行中的每个地点
location_i,输出键值对<location_i, 1>。
- 公式/逻辑:对于输入行中的每个地点
-
Reducer阶段:接收来自Mapper的键值对。Reducer会将具有相同键(即相同地点)的所有值(都是
1)进行累加求和。- 公式:
sum = Σ(value_i),其中所有value_i都等于1。最终输出<location, sum>。
- 公式:
代码实现详解
以下是MapReduce各组件类的具体实现。

Mapper类
Mapper类负责读取数据并生成中间键值对。
public class MyMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
private final static IntWritable one = new IntWritable(1);
private Text location = new Text();
public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
// 1. 将每行文本按星号(*)分割成字段
String[] fields = value.toString().split("\\*");
// 假设字段顺序为:图书ID, 书名, 类别, 作者, 地点, 评论
String locationStr = fields[4]; // 获取地点字段
// 2. 地点字段可能包含多个地点,用逗号分隔
String[] multiLocation = locationStr.split(",");
// 3. 遍历每个地点,输出 <地点, 1>
for (String loc : multiLocation) {
location.set(loc.trim()); // 去除空格并设置为Text类型
context.write(location, one);
}
}
}


Reducer类

Reducer类负责对相同键的值进行聚合计算。
public class MyReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
private IntWritable result = new IntWritable();
public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
int sum = 0;
// 1. 遍历传入的值的集合(全部为1)
for (IntWritable val : values) {
sum += val.get(); // 2. 累加求和
}
// 3. 输出最终结果:<地点, 总数>
result.set(sum);
context.write(key, result);
}
}
Driver类
Driver类是程序的入口,负责配置和提交MapReduce作业。
public class MyDriver {
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf, "Analyze Book Data by Location");
job.setJarByClass(MyDriver.class);
job.setMapperClass(MyMapper.class);
job.setReducerClass(MyReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
// 设置输入和输出路径
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
// 设置Reducer任务数量
job.setNumReduceTasks(1);
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
执行与验证

程序编写完成后,需要打包并提交到Hadoop集群运行。


以下是执行步骤:
- 将Java项目导出为JAR文件(例如
book-analysis.jar)。 - 将JAR文件上传到Hadoop集群主节点。
- 确保Hadoop服务(如NameNode, DataNode)正在运行。
- 使用
hadoop jar命令提交作业。输入路径是上一节生成的扁平化数据文件,输出路径是一个新目录。hadoop jar book-analysis.jar MyDriver /user/input/flat_book_data /user/output/location_analysis - 作业完成后,查看输出目录中的结果文件。
hadoop fs -cat /user/output/location_analysis/part-r-00000
预期输出格式类似:
America 2
Germany 3
India 3
England 1
...
这表示在美国有2个用户记录,在德国有3个,依此类推。

总结


本节课中我们一起学习了如何使用MapReduce进行基于地理位置的数据分析。我们实现了Mapper来提取和拆分地点数据,Reducer来统计每个地点的出现次数。通过这个简单的例子,我们可以将同样的逻辑应用于海量数据,为出版商或网站提供有价值的用户地域分布洞察。下一节,我们将开始学习使用Apache Pig来处理和分析相同的数据集。
009:在Pig中处理XML文件
在本节课中,我们将学习如何使用Apache Pig处理半结构化的XML数据。我们将从加载XML文件开始,逐步学习如何提取数据、分析数据,并最终将结果存储起来。课程将涵盖Pig的基本概念、处理XML所需的特殊加载器,以及如何编写和使用用户自定义函数(UDF)来实现复杂的数据分析逻辑。
什么是Apache Pig? 🐷
上一节我们介绍了使用MapReduce处理XML文件的方法。本节中,我们来看看另一种强大的工具——Apache Pig。
Apache Pig是一种数据流语言,最初由Yahoo开发,后来由Apache基金会接管。它主要为那些不想编写冗长代码或不熟悉Java、Python、Scala等编程语言的用户设计。Pig允许用户或开发者通过定义数据流(即一系列关系)来处理数据。Pig中的数据仅存在于当前会话中,会话结束后数据将丢失。
Pig的核心概念是关系。一个Pig脚本通常包含多个相互关联的关系。例如:
- 关系S1:加载XML文件。
- 关系S2:对S1中的数据执行操作(如按位置分析)。
- 关系S3:将结果输出到控制台。
准备工作:注册Piggy Bank JAR
与MapReduce类似,Pig本身不直接支持处理XML文件。我们需要借助一个包含XMLLoader等函数的库——piggybank.jar。
以下是获取和注册该JAR文件的步骤:
- 从互联网下载
piggybank.jar文件。 - 将其复制到Hadoop系统中。
- 在Pig Grunt Shell中,使用
REGISTER命令注册该JAR文件。
REGISTER /path/to/piggybank.jar;
加载并提取XML数据 📂


注册好JAR文件后,我们就可以开始处理XML数据了。处理过程分为两步:首先加载整个父标签下的数据,然后使用正则表达式提取所需的特定节点数据。

假设我们的XML文件结构如下,父标签是<book>:
<book>
<bookid>1</bookid>
<bookname>Book1</bookname>
<category>Fiction</category>
<author>Author1</author>
<location>India</location>
<review>Awesome|Average|Good|Bad|Very Good</review>
</book>
以下是相应的Pig Latin脚本:


-- 关系 S1: 加载XML文件
-- 使用 XMLLoader,并指定父标签为 ‘book‘
S1 = LOAD ‘/input/books_data.xml‘ USING org.apache.pig.piggybank.storage.XMLLoader(‘book‘) AS (content:chararray);


-- 关系 S2: 使用正则表达式提取各个字段
-- 注意:Pig中没有string类型,使用chararray代替
S2 = FOREACH S1 GENERATE
REGEX_EXTRACT(content, ‘<bookid>(.*?)</bookid>‘, 1) AS bookid:int,
REGEX_EXTRACT(content, ‘<bookname>(.*?)</bookname>‘, 1) AS bookname:chararray,
REGEX_EXTRACT(content, ‘<category>(.*?)</category>‘, 1) AS category:chararray,
REGEX_EXTRACT(content, ‘<author>(.*?)</author>‘, 1) AS author:chararray,
REGEX_EXTRACT(content, ‘<location>(.*?)</location>‘, 1) AS location:chararray,
REGEX_EXTRACT(content, ‘<review>(.*?)</review>‘, 1) AS review:chararray;
-- 关系 S3: 选择并查看部分字段
S3 = FOREACH S2 GENERATE bookname, author, review;
DUMP S3;
执行DUMP S3;后,可以在控制台看到提取出的书名、作者和评论数据。


使用UDF进行数据分析 🔍
我们已经成功提取了原始数据。现在,假设我们想分析每本书的评价表现,即统计每条评论中正面、负面和中性评价的数量。Pig的内置函数可能无法直接完成这种复杂逻辑,这时就需要编写用户自定义函数。


UDF主要有两种类型:
- FilterFunc:用于过滤数据(返回布尔值)。
- EvalFunc:用于处理数据并返回新值(如字符串、数值)。
我们的需求是分析评论字符串,因此选择扩展EvalFunc类。

以下是一个Java UDF示例(AnalyzeBookData),它统计评论字符串中正面、负面和中性词的数量:
package com.example.pig.udf;
import org.apache.pig.EvalFunc;
import org.apache.pig.data.Tuple;
import java.io.IOException;
public class AnalyzeBookData extends EvalFunc<String> {
@Override
public String exec(Tuple input) throws IOException {
if (input == null || input.size() == 0) {
return null;
}
String reviewStr = (String) input.get(0);
int positiveCount = 0;
int negativeCount = 0;
int averageCount = 0;
String[] reviews = reviewStr.split("\\|");
for (String singleReview : reviews) {
if (singleReview.matches("(?i)awesome|excellent|amazing|very good")) {
positiveCount++;
} else if (singleReview.matches("(?i)bad|poor|terrible")) {
negativeCount++;
} else {
averageCount++;
}
}
String result = "Positive=" + positiveCount + ", Negative=" + negativeCount + ", Average=" + averageCount;
return result;
}
}
编写并编译UDF后,需要将其导出为JAR文件(例如pig_udf.jar),然后像注册piggybank.jar一样在Pig脚本中注册它。
REGISTER /path/to/pig_udf.jar;
注册后,就可以在Pig Latin脚本中调用这个UDF了:
-- 继续使用之前的关系 S2
-- 关系 S4: 使用UDF分析评论
S4 = FOREACH S2 GENERATE bookname, com.example.pig.udf.AnalyzeBookData(review) AS review_analysis;
DUMP S4;
执行后,输出将显示每本书名及其对应的评价分析结果,例如:Positive=2, Negative=1, Average=2。

存储处理结果 💾
最后,我们可能希望将分析结果保存到HDFS中,以供后续使用,而不是仅仅打印在控制台。这可以通过STORE命令实现。


-- 将关系 S4 的结果存储到 HDFS
STORE S4 INTO ‘/output/book_review_analysis‘;
执行后,可以在HDFS的指定目录下找到输出文件。


总结
本节课中我们一起学习了如何在Apache Pig中处理XML文件。我们从了解Pig作为一种数据流语言的基本概念开始,然后逐步实践了以下关键步骤:
- 注册包含
XMLLoader的piggybank.jar以支持XML加载。 - 使用
LOAD和REGEX_EXTRACT加载并提取XML数据。 - 为了执行复杂的数据分析(如评论情感统计),我们编写、编译、注册并调用了一个Java用户自定义函数(UDF)。
- 最后,使用
STORE命令将最终结果持久化存储到HDFS中。


通过本教程,你掌握了使用Pig处理半结构化数据的基本流程,并了解了如何通过UDF扩展Pig的功能以满足特定分析需求。
010:使用Pig处理XML文件输出
在本节课中,我们将学习如何进一步处理上一节中从XML文件解析得到的数据。我们将重点介绍Pig中的两个核心函数:TOKENIZE和FLATTEN,并演示如何利用它们将复杂的数据结构(如包含多个值的字段)转换为易于分析的格式。


上一节我们介绍了如何使用自定义函数(UDF)处理XML文件并存储结果。本节中,我们来看看如何对已处理的数据进行更深入的分析,特别是处理那些包含多个值(如以管道符分隔的位置或评论)的字段。
数据回顾与目标
我们之前处理XML文件后,得到了以下格式的数据:
- Book ID:书籍ID
- Category:书籍类别
- Locations:多个位置,以管道符
|分隔(例如:America|Germany|Paris|India|England)

我们的目标是分析每个位置(Location)出现的次数。由于所有位置都挤在单个字段里,直接分析很困难。因此,我们需要先将这些位置拆分成独立的行。

核心函数:TOKENIZE 与 FLATTEN
为了实现数据拆分,我们将使用Pig Latin中的两个内置函数。
1. TOKENIZE 函数
TOKENIZE 函数的作用是将一个字符串按照指定的分隔符拆分成多个词元(token),并将这些词元封装在一个包(bag) 中。在Pig中,一行数据称为一个元组(tuple),一个字段称为一个原子(atom)。TOKENIZE 处理一个原子(字符串),并生成一个包含多个原子的包。
公式/代码描述:
-- 假设有一个字段 `locations` 值为 ‘A|B|C’
tokenized_data = TOKENIZE(locations, ‘|’);
-- 结果:一个包 {(‘A’), (‘B’), (‘C’)},这个包属于原元组的一个字段。


2. FLATTEN 函数


FLATTEN 函数用于“展平”嵌套的数据结构,如包(bag)或元组(tuple)。当对一个包含包的字段使用 FLATTEN 时,Pig会为包中的每个元素生成一个新的独立元组,并复制其他字段的值。
公式/代码描述:
-- 接上例,`tokenized_data` 是一个包字段
flattened_data = FOREACH source_data GENERATE book_id, FLATTEN(tokenized_data) AS location;
-- 结果:为包中每个元素生成新行
-- (book_id1, ‘A’)
-- (book_id1, ‘B’)
-- (book_id1, ‘C’)


实践步骤:拆分与统计位置
以下是使用 TOKENIZE 和 FLATTEN 处理位置数据并统计的完整步骤。
步骤 1: 加载数据并提取字段
首先,我们注册必要的JAR包和UDF,然后加载上一节存储的XML处理结果。
REGISTER /path/to/piggybank.jar;
REGISTER /path/to/your_udf.jar;
-- 假设UDF函数名为 `XmlLoader`
A = LOAD ‘/user/hadoop/xml_output’ USING XmlLoader() AS (book_id:int, category:chararray, locations:chararray);

步骤 2: 使用 TOKENIZE 拆分位置字符串

接下来,我们使用 TOKENIZE 函数。为了正确分隔,我们先将管道符 | 替换为 TOKENIZE 默认识别的空格(或指定逗号分隔)。
B = FOREACH A GENERATE
book_id,
category,
TOKENIZE(REPLACE(locations, ‘|’, ‘,’)) AS location_bag;
执行 DESCRIBE B; 和 DUMP B;,可以看到 location_bag 字段的类型是 {tuple of (chararray)},即一个由元组构成的包。所有位置都被打包在原始数据每一行的这个字段中。
步骤 3: 使用 FLATTEN 展平数据
为了使每个位置成为独立的行,我们对包使用 FLATTEN 操作。
C = FOREACH B GENERATE
book_id,
category,
FLATTEN(location_bag) AS single_location;
执行 DUMP C;,现在数据已经展开。原来的一行数据(例如,book_id=1001,位置包含5个国家)现在变成了五行数据,每行具有相同的 book_id 和 category,但 single_location 分别是 America、Germany 等。


步骤 4: 分组与统计

数据展平后,按位置进行分组和计数就变得非常简单。

D = GROUP C BY single_location;
E = FOREACH D GENERATE
group AS location,
COUNT(C.book_id) AS record_count;
DUMP E;
输出结果将显示每个位置出现的总记录数,例如:(America, 2),(India, 4) 等。
处理评论字段
同样的方法可以应用于评论字段。假设原始数据中 reviews 字段也是管道符分隔(如 awesome|average|good|bad|very good)。
处理流程完全一致:
- 使用
TOKENIZE(REPLACE(reviews, ‘|’, ‘,’))将评论字符串拆分成包。 - 使用
FLATTEN将每条评论展平成独立行。 - 使用
GROUP BY和COUNT统计每种评论出现的次数。
注意:Pig 默认是大小写敏感的。例如,“Average” 和 “average” 会被视为两种不同的评论进行统计。
同时处理多个多值字段


可以同时对多个字段(如 locations 和 reviews)应用 FLATTEN 操作。但这会产生这些字段的笛卡尔积,即每个位置会与每条评论组合。

-- 同时展平位置和评论
F = FOREACH A GENERATE
book_id,
FLATTEN(TOKENIZE(REPLACE(locations, ‘|’, ‘,’))) AS loc,
FLATTEN(TOKENIZE(REPLACE(reviews, ‘|’, ‘,’))) AS rev;
结果行数会显著增加(位置数 × 评论数),适用于特定的关联分析场景。

存储分析结果
与之前一样,我们可以将最终的分析结果存储到 HDFS,以便后续使用。
STORE E INTO ‘/user/hadoop/analysis/location_count’ USING PigStorage(‘,’);
存储后,可以通过HDFS命令查看或下载结果文件。
总结
本节课中我们一起学习了:
TOKENIZE函数:用于将包含分隔符的字符串字段拆分成一个词元包。FLATTEN运算符:用于将包中的元素展开,使每个元素形成独立的行,这是将复杂嵌套数据转换为扁平表结构的关键步骤。- 组合应用:通过
TOKENIZE和FLATTEN的组合,我们能够轻松处理XML或JSON中常见的多值字段,为后续的GROUP BY和聚合分析(如COUNT)做好准备。 - 实际工作流:我们实践了从加载数据、拆分字段、展平数据到分组统计的完整分析流程,并将结果持久化存储。

掌握这些技巧,能够极大地增强我们在Pig中处理半结构化数据的能力,为大数据分析中的维度拆分和指标统计打下坚实基础。
011:利用Hive理解复杂数据集
在本节课中,我们将学习如何使用Hive来分析复杂的数据集。我们将重点介绍Hive中的复杂数据类型,并演示如何将Pig生成的输出作为Hive的输入,通过处理和分析来理解数据。
概述
之前,我们使用MapReduce和Pig处理并分析了基于位置和评论的数据集。现在,我们将转向Hive来完成同样的分析任务。Hive是一个数据分析工具,它不直接处理原始Excel文件,而是使用Pig生成的输出作为输入。我们将学习如何处理这种包含复杂结构(如数组)的数据,并将其转换为易于分析的格式。
Hive与MapReduce、Pig的对比
在深入处理数据之前,我们先来理解Hive与MapReduce和Pig的主要区别,这有助于我们选择适合的工具。
以下是七点关键区别:
-
抽象级别
- MapReduce是低级抽象。我们需要编写详细的Java、Scala或Python代码来处理数据,例如将文本转换为字符串并按分隔符分割。
- Pig是高级抽象。我们只需编写少量Pig Latin语句(关系操作),所有复杂的MapReduce逻辑都被隐藏。
- Hive是高级抽象。我们使用类似SQL的HiveQL语言编写查询,它内部会执行MapReduce作业。
-
数据处理语言
- MapReduce是数据处理语言,可以处理结构化、半结构化和非结构化数据(如文本、图像、PDF)。
- Pig是数据流语言。我们定义数据的处理流程(加载、过滤、分组),但不控制内部执行细节。
- Hive是数据声明式语言。我们通过声明式的SQL语句(如SELECT、WHERE、GROUP BY)来执行操作。
-
语言类型
- MapReduce是编译型语言(需要编译Java代码并生成JAR文件)。
- Pig是脚本语言(使用Pig Latin脚本)。
- Hive使用类SQL的查询语言。
-
代码量
- MapReduce需要大量代码。
- Pig只需要少量语句。
- Hive只需要少量查询。
-
代码性能
- MapReduce的代码性能高,因为开发者完全控制执行逻辑。
- Pig和Hive的性能取决于其内部实现,开发者控制较少。
-
适用场景
- MapReduce适合处理复杂的自定义逻辑及非结构化数据。
- Pig便于执行连接(Join) 操作。
- Hive专为数据分析和聚合查询设计。

- 处理的数据类型
- MapReduce和Pig能处理结构化和非结构化数据。
- Hive主要处理结构化数据,这也是我们使用Pig的输出而非原始Excel文件作为Hive输入的原因。
理解了这些区别后,我们现在可以开始学习如何在Hive中处理复杂数据。

Hive中的复杂数据类型


为了处理我们的数据集(其中“位置”字段包含多个用“|”分隔的值),我们需要先了解Hive的复杂数据类型。Hive主要有三种复杂类型:数组(Array)、映射(Map) 和结构体(Struct)。

1. 数组类型




数组允许在单个列中存储多个值。例如,位置“America|Germany|Paris|India|England”可以视为一个字符串数组。

创建表并加载数据:
CREATE TABLE tbl_array (
id INT,
name STRING,
sub ARRAY<STRING>,
city STRING
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
COLLECTION ITEMS TERMINATED BY '$';
这里,sub列被定义为ARRAY<STRING>。COLLECTION ITEMS TERMINATED BY ‘$’ 指定了数组中每个元素的分隔符是美元符号$。


查询数组元素:
数组元素通过索引访问,索引从0开始。
-- 获取每行数组的第二个元素(索引1)
SELECT sub[1] FROM tbl_array;
-- 获取每行数组的第一个元素(索引0)
SELECT sub[0] FROM tbl_array;


2. 映射类型


映射以键值对的形式存储数据。例如,“Pf#500,EPF#200”表示键Pf对应值500,键EPF对应值200。
创建表并加载数据:
CREATE TABLE tbl_map (
id INT,
name STRING,
sal MAP<STRING, INT>,
city STRING
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
COLLECTION ITEMS TERMINATED BY '$'
MAP KEYS TERMINATED BY '#';
sal列被定义为MAP<STRING, INT>。MAP KEYS TERMINATED BY ‘#’ 指定了键值对中键和值的分隔符。






通过键查询值:
-- 获取键‘Pf’对应的值
SELECT sal[‘Pf’] FROM tbl_map;
-- 获取键‘EPF’对应的值
SELECT sal[‘EPF’] FROM tbl_map;



3. 结构体类型


结构体允许在单个列中定义一组具有特定类型的命名字段。例如,“Bangalore$Karnataka$560001”可以定义为一个包含城市、州和邮编的结构。
创建表并加载数据:
CREATE TABLE tbl_struct (
id INT,
name STRING,
address STRUCT<city:STRING, state:STRING, pin:INT>
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
COLLECTION ITEMS TERMINATED BY '$';
address列被定义为STRUCT<city:STRING, state:STRING, pin:INT>。
查询结构体字段:
使用列名.字段名的格式访问。
-- 获取城市信息
SELECT address.city FROM tbl_struct;
-- 获取州信息
SELECT address.state FROM tbl_struct;


掌握了这些复杂数据类型的基本操作后,我们就可以开始处理实际的数据了。


实战:分析Pig输出数据


现在,我们将使用Hive处理Pig生成的输出文件。该文件包含三列:书籍ID、书籍名称和位置(多个位置用“|”分隔)。我们的目标是将位置数组“展开”,使每个位置独占一行,以便进行聚合分析。
步骤1:准备数据与环境
首先,将Pig的输出文件从本地系统复制到HDFS,并创建一个Hive数据库。
# 将文件复制到HDFS(假设文件名为 part-m-00000)
hadoop fs -put part-m-00000 /user/hive/
# 启动Hive shell
hive
# 创建数据库
CREATE DATABASE book_analysis;
USE book_analysis;


步骤2:创建Hive表
创建一个表,其中loc列定义为字符串数组,以匹配输入文件中用“|”分隔的位置数据。
CREATE TABLE tbl_book (
id INT,
name STRING,
loc ARRAY<STRING>
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
COLLECTION ITEMS TERMINATED BY ‘|’;
步骤3:加载数据
将HDFS上的数据加载到刚创建的表中。
LOAD DATA INPATH ‘/user/hive/part-m-00000’ OVERWRITE INTO TABLE tbl_book;
查询tbl_book表,可以看到loc列现在以数组形式显示(例如 [“America”, “Germany”, “Paris”, “India”, “England”])。

步骤4:展开数组数据
使用Hive的EXPLODE函数和LATERAL VIEW将数组中的每个元素转换为单独的行。
SELECT id, name, loc_single
FROM tbl_book
LATERAL VIEW EXPLODE(loc) loc_table AS loc_single;
执行后,原来的一行数据(包含5个位置)会变成5行数据,每行包含相同的书籍ID和名称,但只有一个位置。

步骤5:存储展开后的数据并进行分析
为了便于后续分析,我们将展开后的数据插入到一个新表中。
-- 创建用于存储中间结果的简单表
CREATE TABLE tbl_intermediate (
id INT,
name STRING,
loc STRING
);

-- 将展开的数据插入新表
INSERT OVERWRITE TABLE tbl_intermediate
SELECT id, name, loc_single
FROM tbl_book
LATERAL VIEW EXPLODE(loc) loc_table AS loc_single;
现在,我们可以轻松地对tbl_intermediate表进行聚合查询。例如,统计每个位置出现的次数:
SELECT loc, COUNT(*) AS count
FROM tbl_intermediate
GROUP BY loc;
此查询将按位置分组并计数,例如 America: 2, Brazil: 1,从而让我们清晰地了解数据的分布情况。



对于“评论”字段,我们可以遵循完全相同的步骤:将其定义为数组,使用EXPLODE函数展开,然后进行各种分析。
总结

在本节课中,我们一起学习了如何利用Hive处理复杂数据集。我们首先对比了Hive、MapReduce和Pig的特点与适用场景。然后,深入探讨了Hive的三种复杂数据类型:数组(Array)、映射(Map) 和结构体(Struct),并学习了如何创建表、加载数据以及查询这些类型。


最后,我们进行了一个完整的实战演练:将Pig的输出作为Hive输入,通过定义数组列、使用EXPLODE函数展开数据,最终成功对书籍位置信息进行了聚合分析。这个方法同样适用于数据集中的其他复杂字段(如评论)。掌握这些技能,你就能有效地使用Hive将复杂数据转换为简单格式,从而执行深入的数据分析。

浙公网安备 33010602011771号