必知:在 Hive 中处理大数据的技术
原文:
towardsdatascience.com/must-know-techniques-for-handling-big-data-in-hive-fa70e020141d
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/8e9346e3b89821d60f53b5e7dab035a0.png
图片由 Christopher Gower 在 Unsplash 上提供
在大多数科技公司中,数据团队必须具备强大的管理和处理大数据的能力。因此,对这些团队来说,熟悉 Hadoop 生态系统是必不可少的。Apache 开发的 Hive 查询语言(HQL)是数据专业人士在 Hadoop 生态系统中操纵、查询、转换和分析数据的强大工具。
HQL 提供了一个类似 SQL 的接口,使得在 Hadoop 中进行数据处理对广泛的用户既易于访问又用户友好。如果你已经精通 SQL,你可能会发现过渡到 HQL 并不困难。然而,需要注意的是,HQL 包含许多独特的函数和特性,这些在标准 SQL 中是不可用的。在这篇文章中,我将根据我的先前经验探讨一些关键的 HQL 函数和特性,这些需要超出 SQL 的特定知识。理解和利用这些能力对于任何使用 Hive 和大数据的人来说至关重要,因为它们构成了在 Hadoop 生态系统中构建可扩展和高效数据处理管道和分析系统的基石。为了说明这些概念,我将提供带有模拟数据和相应的 HQL 语法的用例,以展示如何有效地应用这些特性。
PARTITIONED BY
让我们想象一家电子商务公司需要每天更新其销售数据。如果所有数据都存储在一个未分区的单一数据集中,几个月后表可能会变得非常大,这会显著降低查询性能。
Apache Hive 使用PARTITIONED BY子句来创建分区表,允许查询跳过无关的数据子集,从而提高效率。
在这个例子中,我首先创建了一个名为ecom_sales的表,该表包括SKU_id列——每个 SKU 的唯一标识符,units_sold列——特定 SKU 的销售单位数,以及price_USD列——每单位的价格。该表还包括一个分区列ymd。接下来,我在ymd = 20240801的分区中插入值。最后,我查询了这个分区中的所有数据。
-- Create a partitioned table for sales
CREATE TABLE IF NOT EXISTS ecom_sales (
SKU_id STRING,
unit_sold INT,
price_USD DOUBLE
)
PARTITIONED BY (ymd INT)
STORED AS PARQUET;
-- Insert data into the partitioned table dated August 1st, 2024
INSERT INTO ecom_sales PARTITION (ymd=20240801)
VALUES
('S0002D', 5, 15.99),
('S0002D', 6, 15.99),
('S0001D', 12, 7.99),
('S0003D', 10, 21.50),
('S0001D', 9, 7.99),
('S0001D', 10, 7.99);
-- Query all data from the partition table dated August 1st, 2024
SELECT * FROM ecom_sales
WHERE ymd=20240801;
在 Hive 中,分区无疑通过避免全表扫描来提高查询速度。它还使得定位和修改特定记录变得更加容易。通过分区,你可以通过在非重叠分区上运行操作(如UPDATE和DELETE)来防止并发问题。
然而,对小文件问题保持谨慎是很重要的。最好是创建 100 个每个 1GB 的分区,而不是 10,000 个每个 0.01GB 的分区。小文件可能会引起几个问题:
· 查询性能降低:大量分区会增加 Hive 元数据处理的负担,从而减慢查询规划和执行速度。Hadoop 的分布式处理针对大文件进行了优化,而小文件可能导致过度的磁盘 I/O、网络开销和不必要的 MapReduce 任务。
· 浪费的存储空间:在 HDFS 中,每个文件,无论其大小如何,都占用一个完整的存储块,通常是 128 MB 或 256 MB。小文件会导致存储空间使用效率低下。
· 管理挑战:管理大量小文件会使得优化、数据合并、压缩和平衡等任务变得复杂,使得维护更加困难。
存储为
Hive 使用STORED AS子句来指定表存储的文件格式。文件格式的选择可以显著影响查询性能和存储效率。
在前面的PARTITIONED BY子句部分,我创建了一个名为ecom_sales的分区表,并存储为 PARQUET 格式。
-- Create a partitioned table for sales with file format PARQUET
CREATE TABLE IF NOT EXISTS ecom_sales (
SKU_id STRING,
unit_sold INT,
price_USD DOUBLE
)
PARTITIONED BY (ymd INT)
STORED AS PARQUET;
除了 PARQUET 之外,Hive 还支持几种其他文件格式:
-
PARQUET:一种针对分析工作负载和复杂查询优化的列式存储格式。
-
ORC(优化行列式):由于其高压缩率和快速的读写性能,非常适合写密集型工作负载和事务处理。
-
AVRO:一种基于行的格式,支持结构化和非结构化数据,使其适合于模式演变。
-
TEXTFILE:用于纯文本的格式,通常用于 CSV 或 TSV 文件。如果没有指定,它是默认格式。
-
JSONFILE:用于存储 JSON 格式的数据。
除了这五种常用的格式之外,Hive 还支持其他格式,例如:SEQUENCEFILE(由二进制键/值对组成的平面文件),RCFILE(记录列式文件),INPUTFORMAT和OUTPUTFORMAT用于自定义文件格式和可指定压缩的压缩格式。
在 Hive 中创建新表时,选择适当的格式对于确保高效的数据摄取、存储、查询和处理至关重要。
分布 BY / 聚合 BY
Hive 使用 DISTRIBUTE BY 子句将行分布到 reducer 中。
SET mapreduce.job.reduces=2;
SELECT * FROM ecom_sales
WHERE ymd=20240801
DISTRIBUTE by SKU_id;
在上面的示例中,我将 reducer 的数量设置为 2。在应用DISTRIBUTE BY子句后,所有具有相同SKU_id的行将由同一个 reducer 处理,而不同的SKU_id值可能会被发送到不同的 reducer。例如,具有SKU_id S0001D的行将进入一个 reducer,S0002D进入另一个 reducer,而‘S0003D’进入另一个。S0001D和S0002D最终是否在同一个或不同的 reducer 上取决于 Hive 使用的哈希函数。
您还可以将SORT BY子句添加到语法中,它将在每个 reducer 内部对数据进行排序。
SET mapreduce.job.reduces=2;
SELECT * FROM ecom_sales
WHERE ymd = 20240801
DISTRIBUTE BY SKU_id
SORT BY SKU_id;
结果将如下所示:
https://idc.madlive.cn/wp-content/uploads/2026/02/3a781860799ccd19b57f7f09000c5413.png
作者图片
或者,您可以使用CLUSTER BY来结合DISTRIBUTE BY和SORT BY的功能,产生相同的输出。
SET mapreduce.job.reduces=2;
SELECT * FROM ecom_sales
WHERE ymd = 20240801
CLUSTER BY SKU_id;
LATERAL VIEW 与 EXPLODE
LATERAL VIEW 与 EXPLODE 函数的组合是 Hive 处理数组或映射数据类型的一个强大功能。它允许您将嵌套数据结构扁平化到更传统的关联格式中,使得分析复杂数据更加容易。为了演示这个功能,我创建了一个名为products的另一个表,具有以下列:SKU_id——每个 SKU 的唯一标识符,SKU_name——与 SKU 关联的产品名称,以及description——包含该 SKU 产品描述的数组。
CREATE TABLE products (
SKU_id STRING,
SKU_name STRING,
description ARRAY<STRING>
);
INSERT INTO products VALUES
('S0001D', 'white_shirt_small',ARRAY('shirt','white','small')),
('S0002D', 'blue_pants_large',ARRAY('pants','blue','large')),
('S0003D', 'yellow_jacket_large',ARRAY('pants','blue','large'));
SELECT * FROM products;
上面的语法创建了一个看起来像的表:
https://idc.madlive.cn/wp-content/uploads/2026/02/86f534343845ed4676ad670bddc9a219.png
图片由作者提供
接下来,我使用 EXPLODE 函数将数组description分解成单独的行,其中数组的每个元素都成为单独的行。然后使用 LATERAL VIEW 为原始products表中的数组生成的每一行创建一个虚拟表,名为featureTable。
SELECT p.SKU_id, p.SKU_name, feature
FROM products p
LATERAL VIEW EXPLODE(p.description) featureTable AS feature;
查询结果将看起来像:
https://idc.madlive.cn/wp-content/uploads/2026/02/fa054f4ee2e464ef5f4f6fa06de2693c.png
图片由作者提供
COLLECT_SET
COLLECT_SET 函数是 Hive 中的一个聚合函数,它将唯一元素收集到一个数组中,有效地执行了LATERAL VIEW与EXPLODE函数的逆操作。下面的语法创建了一个名为prod_features的表,其中特定元素将被分组到数组中。
CREATE TABLE prod_features(
SKU_id STRING,
SKU_name STRING,
feature STRING
);
INSERT INTO prod_features VALUES
('S0001D', 'white_shirt_small','shirt'),
('S0001D', 'white_shirt_small','white'),
('S0001D', 'white_shirt_small','small'),
('S0002D', 'blue_pants_large','pants'),
('S0002D', 'blue_pants_large','blue'),
('S0002D', 'blue_pants_large','large'),
('S0003D', 'yellow_jacket_large','jacket'),
('S0003D', 'yellow_jacket_large','blue'),
('S0003D', 'yellow_jacket_large','large');
SELECT * FROM prod_features;
https://os.v.madlive.cn/idcmadlive/2026/02/178589efbc80238c1bf2ce5fe4142ed8.png
prod_features(图片由作者提供)
以下语法演示了 COLLECT_SET 函数如何将feature列中的所有唯一值聚合到一个数组中。
SELECT SKU_id, SKU_name, COLLECT_SET(feature) AS description
FROM prod_features
GROUP BY SKU_id, SKU_name;
查询结果将显示为:
https://os.v.madlive.cn/idcmadlive/2026/02/701345d5d3cb9203683cd41f780eafd5.png
图片由作者提供
结论
本文讨论了 5 个 Hive 特定功能,这些功能使 Hive 与 SQL 不同,使其特别适合大数据处理和分析。
-
PARTITIONED BY通过优化数据存储和检索,使大型数据集的管理更加高效。
-
存储为通过使用针对工作负载定制的特定文件格式,提高了存储效率和查询性能。
-
DISTRIBUTE BY和CLUSTER BY对于分布式系统中的数据查询性能优化至关重要,因为它们在并行处理期间对 reducer 上数据分布的粒度控制。
-
LATERAL VIEW 与 EXPLODE将数组或映射数据类型转换为单独的行,便于对嵌套数据结构进行详细分析。
-
COLLECT_SET将单个元素聚合到数组中,为汇总和分组数据提供了强大的工具。
通过利用这些 Hive 特定的功能,数据专业人员可以建立更有效和健壮的大数据解决方案,解决仅使用标准 SQL 难以或无法应对的挑战。