On this page
Motivations
Hive 被广泛用作大数据领域中众多不同问题类型的解决方案。很显然,它通常用于大型数据集的临时查询。但是,它也用于实现 ETL 类型的过程。与即席查询不同,为 ETL 编写的 Hive SQL 具有一些独特的属性:
它通常按计划重复执行。
它通常是一个庞大,复杂的代码体。
它具有更长的寿命,可以长期保存在组织的代码库中。
随着时间的推移,经常对其进行修改,并且通常以一种有机方式来满足不断变化的业务需求。
这对组织的运营至关重要,因为它会生成有价值的数据。
表现出这种特性的代码非常适合单元测试,因为它很容易出现错误,错误和意外损坏,所有这些都可能对组织构成风险。
Challenges
Hive 和 Hive SQL 都带来了许多挑战,这使得为基于 Hive 的 ETL 系统构建单元测试套件变得困难。这些可以大致描述如下:
定义组件之间的边界: 如何以及如何将问题分解为较小的可测试单元。 Hive SQL 提供的语言功能集限制了这样做的能力。
线束设置: 提供一个本地执行环境,该环境在本地 IDE 设置(UDF 等)中无缝支持 Hive 的功能。理想情况下,线束应该没有环境依赖性,例如本地 Hive 或 Hadoop 安装。开发人员应该能够简单地签出项目并运行测试。
执行速度: 目标是进行大量独立的小型测试。测试隔离需要频繁进行设置和拆卸,并且所产生的成本会乘以测试数量。 Hive CLI 是重复启动和停止的相当繁重的过程,因此某些 Hive 测试框架试图优化测试执行的这一方面。
Modularisation
通过使用 Hive 实施的模块化流程,它们变得更容易有效地测试,并且对变更具有更大的弹性。尽管 Hive 提供了许多用于模块化的向量,但是并不总是很清楚如何分解大型过程。将查询逻辑封装到组件中的功能分为两个垂直方面:列级逻辑和设置级逻辑。列级逻辑是指应用于查询中单个列或一组列的表达式,通常称为“函数”。集级逻辑涉及 Hive SQL 构造,该构造可操纵数据分组,例如:具有SELECT
,GROUP BY
聚合,JOIN
s,ORDER BY
排序等的列投影。在两种情况下,我们都希望单个组件位于其自己的源文件或可部署工件中并导入根据组成需要。对于基于 Hive SQL 的组件,SOURCE
命令提供了此功能。
列级逻辑的封装
对于列级逻辑,Hive 提供了UDFs和macros,它们允许用户提取并重用应用于列的表达式。一旦定义,UDF 和宏就可以很容易地隔离出来进行测试。可以使用现有的 Java/Python 单元测试工具(例如 JUnit)简单地测试 UDF,而宏则需要 Hive 命令行界面来执行宏声明,然后使用一些示例SELECT
语句对其进行练习。
集合级逻辑的封装
与列级逻辑不同,如何最好地封装和组合基于集合的逻辑的集合要少得多。考虑以下包含连接,分组和列投影的单个复杂查询的示例:
Monolithic query
SELECT ... FROM ( -- Query 1
SELECT ... FROM ( -- Query 2
SELECT ... FROM ( -- Query 3
SELECT ... FROM a WHERE ... -- Query 4
) A LEFT JOIN ( -- Query 3
SELECT ... FROM b -- Query 5
) B ON (...) -- Query 3
) ab FULL OUTER JOIN ( -- Query 2
SELECT ... FROM c WHERE ... -- Query 6
) C ON (...) -- Query 2
) abc LEFT JOIN ( -- Query 1
SELECT ... FROM d WHERE ... -- Query 7
) D ON (...) -- Query 1
GROUP BY ...; -- Query 1
此查询具有非常广泛的职责集,无法轻松地单独进行验证。经过仔细检查,似乎实际上是由至少 7 个不同的查询组成的。为了有效地对该过程进行单元测试,我们必须将每个子查询封装到单独的组件中,以便可以对其进行独立测试。为实现这一目标,我们向您开放了许多方法,包括:
具有中间表的组件的 Sequences 执行。
Views.
查询片段的变量替换。
功能/过程 SQL 方法。
有限的测试表明,就美观和性能而言,VIEW
方法比对带有中间表的组件 Sequences 执行要有效得多。中间表解决方案(包括TEMPORARY
表)需要更长的运行时间,生成更多的 I/O 并限制查询优化的机会。还应该指出的是,观点似乎没有经常提到的性能问题;实际上,视图和整体查询的执行计划和时间是可比的。
还建议使用变量替换作为模块化大型查询的方法,但是在检查时发现它不合适,因为需要额外的 bash 文件,这会使测试更加复杂。还考虑了 HPL/SQL,但是它没有查询模块化所需的必要管线功能。
工具和框架
构造测试时,拥有一个简化测试的声明和执行的框架会很有帮助。通常,这些工具允许指定许多以下内容:
执行环境配置:通常为
hiveconf
和hivevar
参数。声明 Importing 测试数据:创建或选择支持某些源表的文件。
测试的可执行组件的定义:通常是被测试的 SQL 脚本。
期望:这些可以采用参考数据文件的形式,或者可以通过进一步的查询来做出细粒度的 assert。
确切的细节当然是特定于框架的,但是通常来说,工具通过将开发人员提供的工件组合成如下序列来 Management 测试的整个生命周期:
配置 Hive 执行环境。
设置测试 Importing 数据。
执行测试中的 SQL 脚本。
提取执行脚本编写的数据。
对提取的数据进行 assert。
目前,有多种具体方法可供选择:
HiveRunner:测试用例是使用 Java,Hive SQL 和 JUnit 声明的,可以在您的 IDE 中本地执行。该库专注于易用性和执行速度。无需本地 Hive/Hadoop 安装。提供完整的测试隔离,细粒度的 assert 和无缝的 UDF 集成(它们只需要位于项目 Classpath 中)。 Metastore 由内存数据库支持,以提高测试性能。
beetest:使用 Hive SQL 和“预期”数据文件声明测试用例。测试套件是使用命令行上的脚本执行的。显然需要在执行测试的环境中安装 HDFS。
hive_test:测试用例是使用 Java,Hive SQL 和 JUnit 声明的,可以在您的 IDE 中本地执行。
HiveQLUnit:在您喜欢的 IDE 中测试您的 Hive 脚本。似乎使用 Spark 执行测试。
Useful practices
以下 Hive 特定实践可用于使流程更适合于单元测试,并有助于简化单个测试。
将大型或复杂查询模块化为多个较小的组件。这些更易于理解,维护和测试。
使用宏或 UDF 封装重复或复杂的列表达式。
使用Hive variables将 SQL 脚本与特定环境解耦。例如,优先使用_而不是
LOCATION /hard/coded/path
可能是明智的。保持测试范围小。对表的整个内容进行粗 assert 很容易,并且维护要求很高。
使用
SOURCE
命令可以组合多个较小的 SQL 脚本。通过创建简单的测试表并将功能应用于这些表中的列来测试宏和 UDF 的集成。
通过在标准测试框架(例如 JUnit)中直接调用生命周期方法(
initialize
,evaluate
等)来测试 UDF。
Relevant issues
- HIVE-12703:与 CLI 无关的 HQL 导入命令实现
其他 Hive 单元测试问题
尽管与 Hive SQL 并不特别相关,但存在用于测试 Hive 生态系统其他方面的工具。特别是BeeJU项目提供了 JUnit 规则,以简化与 Hive Metastore 和 HiveServer2 服务的集成测试。例如,如果您正在开发旨在利用 Hive 的元数据功能的替代数据处理框架或工具,这些功能将非常有用。