Hive 面试题
一、Hive 基础概念
什么是Hive
Hive 是基于Hadoop的一个数据仓库工具,可以将结构化和半结构化的数据文件映射为一张数据库表,并提供简单的sql查询功能。 注意:
- 1.Hive本质是将HDFS转换成MapReduce的任务进行运算,底层由HDFS来提供数据存储。
- 2.Hive的元数据存储在SQL上,HBase的元数据存储在HDFS上。
Hive表与数据库表之间有哪些区别
存储:Hive表通常存储再分布式文件系统中,如HDFS;数据块表存储再关系型数据库管理系统(RDBMS)中。
查询语言:Hive使用类SQL语言进行数据查询、分析和处理,而数据库表使用结构化查询语言(SQL)。
索引:数据库表经常需要在数据中创建索引以提高性能,而Hive表默认不支持索引。
数据模型:数据库表通常以规范化形式设计,而Hive更适合用来处理扁平的、临时的或者半结构化数据。
Hive的优点和缺点
Hive是一种基于Hadoop的工具,它提供了SQL界面和查询语言来查询和分析 存储在Hadoop上的大规模结构化数据。以下是Hive的优点和缺点:
优点:
- 简化了复杂MapReduce任务:对于熟悉SQL而不是编写MapReduce代码的用户来说,可以更容易地使用SQL语法进行大规模数据分析。
- 支持高度可扩展性:可以运行在一个集群上,并能够以并行方式执行查询,在海量数据情况下保持良好地性能。
缺点:
延迟较高:相对于直接使用Hadoop的原生API,Hive的查询通常具有较高的延迟。这是由于转换SQL查询语句为底层MapReduce任务所带来的额外开销。
不适合实时处理:Hive更适用于批处理任务而不是实时数据处理,因为它不支持动态数据更新和低延迟查询。 限制:在某些情况下,复杂的查询或特殊要求可能无法通过Hive来满足。
Hive中Sort By,Order By,Cluster By,Distribute By分别是什么意思?
Sort By:Sort By用于在查询结果中对列进行排序。它会按照指定的列对查询结果进行排序,默认为升序排序,也可以通过DESC关键词进行降序排序。
Order By:Order By也用于对查询结果进行排序,但不同于Sort By,Order By会在最终的查询结果中对所有的行进行全局排序。这意味着Order By会将整个结果集加载到内存中进行排序,适用于小规模数据集。
Cluster By:Cluster By用于将表的数据按照指定的列进行分区存储。它类似于数据库中的分区表概念,可以提高查询性能。使用Cluster By时,Hive会根据指定的列值进行数据分区,并将相同的值的行存储在同一个分区中。
Distribute By:Distribute By用于将表的数据按照指定的列进行分发。它用于控制数据在不同的Reducer任务中的分布情况。Distribute By确保具有相同分发列值的行都被发送到同一个Reducer任务中,但并不保证在Reducer任务内部的排序。
Hive中row_number,rank和dense rank窗口函数的区别
在Hive中,row_number、rank和dense rank都是窗口函数,用于在查询结果中对数据进行排序和分组。
row_number函数:它为每一行分配一个唯一的整数值,这个值根据窗口排序规则进行排序。即使两个行值相同,它们的row_number值也会不同。例如,如果有5行数据,排序后的结果分别为1、2、3、4、5。
rank函数:它为每一行分配一个排名值,根据窗口排序规则进行排序。如果两个行的值相同,它们的rank值也会相同,而下一个行将跳过对应数量的排名。例如,如果有5行数据,排序后的结果分别为1、2、2、4、5。
dense rank函数:它为每一行分配一个紧凑的排名值,根据窗口排序规则进行排序。即使两个行的值相同,它们的dense rank值也会不
Hive内部表和外部表区别
Hive内部表和外部表的区别在于数据的存储和管理方式。
内部表(Internal Table): 内部表是Hive默认创建的表,它的数据存储在Hive的数据仓库中的默认路径下。Hive负责管理内部表的数据和元数据,包括数据的加载、删除、备份等操作。当删除内部表时,Hive会删除该表的元数据和数据。内部表适用于Hive独立管理数据的场景。
外部表(External Table): 外部表是指在Hive中定义的表,但数据存储在Hive之外的位置,例如HDFS上的指定路径或者其它存储系统中。外部表的元数据由Hive负责管理,但数据本身由外部存储系统管理。当删除外部表时,Hive只删除元数据而不删除实际数据。外部表适用于需要与其它系统共享数据的场景,如与其它工具或框架进行数据交互。
二、Hive架构与执行流程
Hive的执行流程
- 解析器(Parser):Hive首先==通过解析器将用户输入的HiveQL语句转 换为抽象语法树(AST)==的形式。
- 语义分析器(Semantic Analyzer):Hive的语义分析器会对AST进行语义分析,包括验证表和列的存在性、检查数据类型、解析表达式等。
- 查询优化器(Query Optimizer):在语义分析完成后,Hive会对查询进行优化,包括重写查询计划、选择合适的连接方式、重新排序操作等,以提高查询性能。
- 查询计划生成器(Query Plan Generator):优化后的查询计划将被传递给查询计划生成器,生成逻辑查询计划。
- 物理计划生成器(Physical Plan Generator):逻辑查询计划会被传递给物理计划生成器,生成物理查询计划,包括选择合适的物理操作(如MapReduce、Tez等)和任务的划分。
- 执行器(Executor):生成的物理查询计划将被执行器执行,根据计划中的操作类型,将任务提交给相应的计算引擎(如MapReduce、Tez等)进行执行。
- 结果存储:执行完成后,查询结果将被存储在指定的位置,可以是本地文件系统、HDFS等。
Hive SQL转化为MR的过程
- 解析Hive SQL语句:首先,Hive会解析输入的Hive SQL查询语句,识别出查询的表、列以及其它相关的元数据信息。
- 查询优化和逻辑计划生成:Hive会将解析后的查询语句进行查询优化,根据表的统计信息和用户定义的参数选择最佳的执行计划。然后,Hive会生成逻辑查询计划,该计划描述了查询的逻辑执行流程。
- 逻辑计划到物理计划的转化:在这一步骤中,Hive将逻辑查询计划转换为物理查询计划,该 计划描述了如何在MapReduce框架下执行查询。转换的过程通常包括将逻辑操作符映射到对应的MapReduce任务(如Map、Reduce、Join等),以及确定数据的分区和排序策略。
- 生成MapReduce作业:根据转换后的物理查询计划,Hive会生成一系列的MapReduce作业。每个作业会包括一个或多个Map任务和一个Reduce任务,它们负责执行查询并生成结果。
- 执行MapReduce作业:生成的MapReduce作业将被提交给Hadoop集群进行执行。在执行期间,MapReduce框架会负责将输入数据按照指定的分区方式划分到不同的Map任务中,并在Map和Reduce任务之间进行数据的传输和处理。
- 输出结果:一旦所有的MapReduce作业执行完成,Hive会从最后一个Reduce任务中获取最终的查询结果,并将其返回给用户或保存到指定的输出表中。
如何避免Hive中Join操作引起全表扫描?
使用合适的Join算法:根据数据的特点选择合适的Join算法,例如使用Map Join、Sort-Merge Join等。 调整表的存储格式:使用支持索引和列式存储等高效查询的存储格式,如Parquet或ORC。 对Join操作进行拆分:将大表进行水平分片或者预分区(Pre-partitioned)处理,以达到局部性原则,并使用Bucketing对小表数据进行哈希划分。 合理设置Join关联条件:确保关联条件可以做到快速过滤。
Hive如果不用参数调优,在map和reduce端应该做 什么
Map端:
- 增加map任务的数量:可以通过设置’mapred.map.tasks’参数来增加map任务的数量,从而提高并行度和整体处理速度。
- 压缩中间数据:可以使用Hive的压缩功能,如设置’hive.exec.compress.intermediate’参数为true,将中间数据进行压缩,减少磁盘I/O开销。
Reduce端:
- 增加reduce任务的数量:可以通过设置’mapred.reduce.tasks’参数来增加reduce任务的数量,从而提高并行度和整体处理速度。
- 合理设置shuffle阶段的内存大小:可以通过调整’hive.shuffle.memory.limit’参数来控制shuffle阶段的内存大小,避免内存溢出或过多的磁盘I/O操作。
- 使用Combiner函数:如果Reduce端的数据量较大,可以使用Combiner函数来进行部分聚合操作,减少传输给Reducer的数据量,提高性能。
三、Hive 使用
Hive分区和分桶的区别
Hive分区和分桶是Hive中用于优化查询性能的两种技术。
Hive分区: Hive分区是基于数据的某个列(通常是日期、地理位置等)进行的逻辑划分。分区可以将数据按照指定的列值划分成多 个目录或文件,使得查询时只需要扫描特定分区的数据,从而提高查询性能。分区可以在创建表时定义,也可以在已有的表进行动态分区操作。分区的主要目的是减少查询时需要扫描的数据量,提高查询效率。
Hive分桶: Hive分桶是将数据分散存储到多个文件中,每个文件称为一个桶。分桶是通过对某些列的哈希函数计算得到的,可以在创建表时定义分桶数和分桶列。分桶的目的是将数据均匀地分布在多个桶中,使得查询时只需要扫描特定桶地数据,进一步提高查询性能。与分区不同的是,分桶不是将数据按照某些列值进行划分,而是通过哈希函数进行随机分配。
Hive的三种自定义函数是什么?实现步骤与流程?它们之间的区别?作用是什么?
特性 | UDF | UDAF | UDTF |
---|---|---|---|
全称 | User-Defined Function | User-Defined Aggregation Function | User-Defined Table-Generating Function |
输入 | 单行输入 | 多行输入 | 单行输入 |
输出 | 单个值 | 单个聚合值 | 多行和多列 |
主要用途 | 转换或处理单个值 | 聚合计算 | 行转列,数据扩展 |
示例应用 | 字符串处理,日期转换 | 自定义平均 值,中位数计算 | 字符串拆分,JSON解析 |
- UDF (User-Defined Function)
- 实现步骤:
- 创建一个Java类,继承org.apache.hadoop.hive.ql.exec.UDF
- 实现evaluate()方法
- 编译打包成JAR文件
- 在Hive中添加JAR文件并创建函数
- 作用:处理单行输入并返回单个输出值,如字符串操作、数学计算等。
- 实现步骤:
- UDAF (User-Defined Aggregation Function)
- 实现步骤:
- 创建一个Java类,继承org.apache.hadoop.hive.ql.exec.UDAF
- 创建一个内部类继承UDAFEvaluator
- 实现init(), iterate(), terminatePartial(), merge(), terminate()方法
- 编译打包成JAR文件
- 在Hive中添加JAR文件并创建函数
- 作用:对多行输入进行聚合计算,返回单个值,如自定义的平均值、中位数等。
- 实现步骤:
- UDTF (User-Defined Table-Generating Function)
- 实现步骤:
- 创建一个Java类,继承org.apache.hadoop.hive.ql.udf.generic.GenericUDTF
- 实现initialize(), process(), close()方法
- 编译打包成JAR文件
- 在Hive中添加JAR文件并创建函数
- 作用:接受单行输入,输出多行或多列数据,常用于数据扩展或行转列操作。
- 实现步骤:
主要区别:
- 输入输出:UDF处理单行返回单值,UDAF处理多行返回单值,UDTF处理单行返回多行多列。
- 使用场景:UDF适用于简单转换,UDAF用于聚合计算,UDTF用于数据扩展。
- 复杂度:UDF最简单,UDAF和UDTF相对复杂,需要实现更多方法。
这些自定义函数极大地增强了Hive的灵活性和功能性,允许用户根据特定需求扩展Hive的能力,处理复杂的数据转换和分析任务。
四、性能调优
建表设计
1. 利用分区表优化
分区是将数据基于一个或多个列的值分散到不同目录的一种数据组织方式。
优势:
- 显著提高查询性能,特别是对大数据集
- 允许更有效的数据生命周期管理
- 减少全表扫描,降低I/O开销
使用建议:
- 选择高基数(不同值较多)但又不至于产生过多小分区的列作为分区键
- 经常用于过滤条件的列适合作为分区列
- 避免过度分区,可能导致小文件问题
2. 利用分桶表优化
分桶是将数据基于某列的哈希值分散到固定数量的桶中的技术。
优势:
- 提高某些查询的效率,尤其是join操作
- 为抽样查询提供更高效的支持
- 可以实现更细粒度的数据组织
使用建议:
- 选择具有较高基数的列作为分桶列
- 桶的数量通常选择2的幂次方(如64、128等)
- 在有大量join操作的场景下特别有用
3. 选择合适的文件存储格式
Hive支持多种文件格式,包括TextFile、SequenceFile、RCFile、ORC、Parquet等。
优势:
- 不同格式适合不同的数据访问模式
- 列式存储格式(如ORC、Parquet)可大幅提升查询性能
- 某些格式提供更好的压缩率,节省存储空间
使用 建议:
- 对于读密集型查询,推荐使用ORC或Parquet
- TextFile适合简单场景和数据交换
- 考虑数据的结构、查询模式和压缩需求来选择格式
4. 选择合适的压缩格式
压缩可以减少存储空间和网络传输量,但会增加CPU开销。
优势:
- 减少存储空间和网络I/O
- 某些压缩格式支持分片,有利于并行处理
- 可以提高整体查询性能
使用建议:
- 选择压缩比和解压速度平衡的算法,如Snappy或LZO
- 对于冷数据可以使用压缩比更高的算法,如GZIP
- 确保选择的压缩格式与文件存储格式兼容
- 考虑硬件资源,在I/O受限的环境中,压缩更有价值
HQL语法和运行参数层面
1、查看Hive执行计划
Hive 的 SQL 语句在执行之前需要将 SQL 语句转换成 MapReduce 任务,因此需要了解具体的转换过程,可以在 SQL 语句中输入如下命令查看具体的执行计划 。
2、列裁剪
列裁剪就是在查询时只读取需要的列,分区裁剪就是只读取需要的分区。当列很多或者数据量很大时,如果 select * 或者不指定分区,全列扫描和全表扫描效率都很低。
3、谓词下推
将 SQL 语句中的 where 谓词逻辑都尽可能提前执行,减少下游处理的数据量。对应逻辑优化器是 PredicatePushDown
。
## 默认是true
set hive.optimize.ppd=true;
4、分区裁剪
列裁剪就是在查询时只读取需要的列,分区裁剪就是只读取需要的分区 。当列很多或者数据量很大时,如果 select * 或者不指定分区,全列扫描和全表扫描效率都很低 。
在查询的过程中只选择需要的分区,可以减少读入的分区数目,减少读入的数据量 。
Hive 中与分区裁剪优化相关的则是:
## 默认是true
set hive.optimize.pruner=true;
5、合并小文件
Map 输入合并
在执行 MapReduce 程序的时候,一般情况是一个文件的一个数据分块需要一个 mapTask 来处理。但是如果数据源是大量的小文件,这样就会启动大量的 mapTask 任务,这样会浪费大量资源。可以将输入的小文件进行合并,从而减少 mapTask 任务数量 。
## Map端输入、合并文件之后按照block的大小分割(默认)
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
## Map端输入,不合并
set hive.input.format=org.apache.hadoop.hive.ql.io.HiveInputFormat;