【大数据面试题】Hive
1-Hive定义&优缺点
hive是基于Hadoop的一个数据仓库工具,用来进行数据提取、转化、加载,这是一种可以存储、查询和分析存储在Hadoop中的大规模数据的机制。hive数据仓库工具能将结构化的数据文件映射为一张数据库表,并提供SQL查询功能,能将SQL语句转变成MapReduce任务来执行。
- 解决海量结构化日志的数据统计分析工具。
- 数据仓库软件。
- 提供了 SQL 操作分布式存储的大型数据集。
- 将数据映射为逻辑表。
Hive 的本质就是将 HQL 转换成 MapReduce 程序,方便使用。Hive 处理的数据存储在 HDFS,分析数据的底层实现 MapReduce,执行程序运行在 Yarn 上。
优点:
- 操作接口采用类 SQL 语法,提供快速开发的能力(简单、容易上手)。
- 避免了去写 MapReduce,减少开发人员的学习成本。
- Hive 优势在于处理大数据,用于对实时性要求不高的大数据分析,对于处理小数据没有优势。
- Hive 支持用户自定义函数,用户可以根据自己的需求来实现自己的函数。
缺点:
- Hive的HQ表达能力有限,迭代式算法无法表达,数据挖掘方面不擅长(MapReduce 的设计思想限制)。
- Hive的效率比较低,Hive 自动生成的 MapReduce 作业,通常情况下不够智能化,调优比较困难,粒度较粗。
2-Hive的架构
1)用户接口:Client
CLI(command-line interface)、JDBC/ODBC(jdbc 访问 hive)、WEBUI(浏览器访问 hive)
2)元数据:Metastore
元数据包括:表名、表所属的数据库(默认是 default)、表的拥有者、列/分区字段、表的类型(是否是外部表)、表的数据所在目录等;
默认存储在自带的 derby 数据库中,推荐使用 MySQL 存储 Metastore
3)Hadoop
使用 HDFS 进行存储,使用 MapReduce 进行计算。
4)驱动器:Driver
5)解析器(SQL Parser)
将 SQL 字符串转换成抽象语法树 AST,这一步一般都用第三方工具库完成,比如 antlr;
对 AST 进行语法分析,比如表是否存在、字段是否存在、SQL 语义是否有误。
6)编译器(Physical Plan)
将 AST 编译生成逻辑执行计划。
7)优化器(Query Optimizer)
对逻辑执行计划进行优化。
8)执行器(Execution)
把逻辑执行计划转换成可以运行的物理计划。对于 Hive 来说,就是 MR/Spark。
3-HQL转成MR的流程
1.进入程序,利用Antlr框架定义HQL的语法规则,对HQL完成词法语法解析,将HQL转换为为AST(抽象语法树);
2.遍历AST,抽象出查询的基本组成单元QueryBlock(查询块),可以理解为最小的查询执行单元;
3.遍历QueryBlock,将其转换为OperatorTree(操作树,也就是逻辑执行计划),可以理解为不可拆分的一个逻辑执行单元;
4.使用逻辑优化器对OperatorTree(操作树)进行逻辑优化。例如合并不必要的ReduceSinkOperator,减少Shuffle数据量;
5.遍历OperatorTree,转换为TaskTree。也就是翻译为MR任务的流程,将逻辑执行计划转换为物理执行计划;
6.使用物理优化器对TaskTree进行物理优化;
7.生成最终的执行计划,提交任务到Hadoop集群运行。
4-Hive安装部署
Hive安装部署:
解压-配置环境变量-环境变量生效-解决日志Jar包冲突(mv log4j-slf4j-impl-2.10.0.jar log4j-slf4j-impl-2.10.0.jar.bak)-将MySQL的JDBC驱动拷贝到Hive的lib目录下-配置Metastore到MySQL-hive-site.xml 配置数据库的jdbc、root、passwd---初始化元数据库--schematool -initSchema -dbType mysql -verbose
5-Hive和传统数据库比较
Hive和数据库除了拥有类似的查询语言,再无类似之处。
1)数据存储位置
Hive 存储在 HDFS 。数据库将数据保存在块设备或者本地文件系统中。
2)数据更新
Hive中不建议对数据的改写。而数据库中的数据通常是需要经常进行修改的,
3)数据规模
Hive支持很大规模的数据计算;数据库可以支持的数据规模较小。
4)执行延迟
Hive 执行延迟较高。数据库的执行延迟较低。当然,这个是有条件的,即数据规模较小,当数据规模大到超过数据库的处理能力的时候,Hive的并行计算显然能体现出优势。
6-内部表和外部表
创建表:
- Hive创建内部表时,会将数据移动到数据仓库指向的路径,hive管理数据的生命周期;
- Hive创建外部表时,仅记录数据所在的路径,不对数据的位置做任何改变。
删除表:
- Hive删除内部表时,内部表的元数据和数据会一起被删除。同时对于一些hive操作不适应于外部表,比如单个查询语句创建表并向表中插入数据。
- Hive删除外部表时,不删除数据。这样外部表相对来说更加安全些,数据组织也更加灵活,方便共享源数据。创建外部表时,甚至不需要知道外部数据是否存在,可以把创建数据推迟到创建表之后才进行。
7-4个By区别
1)Order By:全局排序,只有一个Reducer;
2)Sort By:分区内有序;
3)Distrbute By:类似MR中Partition,进行分区,结合sort by使用。
4) Cluster By:当Distribute by和Sorts by字段相同时,可以使用Cluster by方式。Cluster by除了具有Distribute by的功能外还兼具Sort by的功能。但是排序只能是升序排序,不能指定排序规则为ASC或者DESC。
在生产环境中Order By用的比较少,容易导致OOM。
在生产环境中Sort By+ Distrbute By用的多。
8-系统函数
1)date_add、date_sub函数(加减日期)
2)next_day函数(周指标相关)
3)date_format函数(根据格式整理日期)
4)last_day函数(求当月最后一天日期)
5)collect_set函数
6)get_json_object解析json函数
7)NVL(表达式1,表达式2)
如果表达式1为空值,NVL返回值为表达式2的值,否则返回表达式1的值。
9-自定义UDF、UDTF函数
UDF:返回对应值,一对一
UDAF:返回聚类值,多对一
UDTF:返回拆分值,一对多
1)在项目中是否自定义过UDF、UDTF函数,以及用他们处理了什么问题,及自定义步骤?
(1)用UDF函数解析公共字段;用UDTF函数解析事件字段。
(2)自定义UDF:继承UDF,重写evaluate方法
- 继承
org.apache.hadoop.hive.ql.exec.UDF
类 - 重写evaluate()方法并实现函数逻辑
- 打
jar
包上传到集群 - 复制到正确的HDFS路径
- 通过
create temporary function
创建临时函数,不加temporary
就创建了一个永久函数; - 通过select 语句使用
(3)自定义UDTF:
UDTF 需要继承 GenericUDTF 抽象类并实现其中的initialize、process和close 方法。
i)initialize 方法传入输入的ObjectInspector,返回类型是StructObjectInspector也就是输出数据的Inspector。
ii)process 处理数据,处理完的数据通过forward 方法将处理完的数据发送给其他算子
iii)当所有行都处理完了调用close()方法
iiii)将编写的类打包,放到 user/hive/jars 路径下。之后创建永久函数与开发好的jar包关联。
2)为什么要自定义UDF/UDTF?
有一些复杂的业务逻辑很难用sql实现,比如ip获取城市
因为自定义函数,可以自己埋点Log打印日志,出错或者数据异常,方便调试。
10-窗口函数
1)Rank
(1)RANK() 排序相同时会重复,总数不会变
(2)DENSE_RANK() 排序相同时会重复,总数会减少
(3)ROW_NUMBER() 会根据顺序计算
2) OVER():指定分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变而变化
(1)CURRENT ROW:当前行
(2)n PRECEDING:往前n行数据
(3) n FOLLOWING:往后n行数据
(4)UNBOUNDED:起点,UNBOUNDED PRECEDING 表示从前面的起点, UNBOUNDED FOLLOWING表示到后面的终点
(5) LAG(col,n):往前第n行数据
(6)LEAD(col,n):往后第n行数据
(7) NTILE(n):把有序分区中的行分发到指定数据的组中,各个组有编号,编号从1开始,对于每一行,NTILE返回此行所属的组的编号。注意:n必须为int类型。
3)手写TopN
11-Hive优化
1)MapJoin
如果不指定MapJoin或者不符合MapJoin的条件,那么Hive解析器会将Join操作转换成Common Join,即:在Reduce阶段完成join。容易发生数据倾斜。可以用MapJoin把小表全部加载到内存在map端进行join,避免reducer处理。
2)行列过滤
列处理:在SELECT中,只拿需要的列,如果有,尽量使用分区过滤,少用SELECT 。
行处理:在分区剪裁中,当使用外关联时,如果将副表的过滤条件写在Where后面,那么就会先全表关联,之后再过滤。
3)列式存储
4)采用分区技术
5)合理设置Map数
mapred.min.split.size: 指的是数据的最小分割单元大小;min的默认值是1B
mapred.max.split.size: 指的是数据的最大分割单元大小;max的默认值是256MB
通过调整max可以起到调整map数的作用,减小max可以增加map数,增大max可以减少map数。
需要提醒的是,直接调整mapred.map.tasks这个参数是没有效果的。
https://www.cnblogs.com/swordfall/p/11037539.html
6)合理设置Reduce数
Reduce个数并不是越多越好
(1)过多的启动和初始化Reduce也会消耗时间和资源;
(2)另外,有多少个Reduce,就会有多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题;
在设置Reduce个数的时候也需要考虑这两个原则:处理大数据量利用合适的Reduce数;使单个Reduce任务处理数据量大小要合适;
7)小文件如何产生的?
(1)动态分区插入数据,产生大量的小文件,从而导致map数量剧增;
(2)reduce数量越多,小文件也越多(reduce的个数和输出文件是对应的);
(3)数据源本身就包含大量的小文件。
8)小文件解决方案
(1)在Map执行前合并小文件,减少Map数:CombineHiveInputFormat具有对小文件进行合并的功能(系统默认的格式)。HiveInputFormat没有对小文件合并功能。
(2)merge
输出合并小文件
SET hive.merge.mapfiles = true; -- 默认true,在map-only任务结束时合并小文件
SET hive.merge.mapredfiles = true; -- 默认false,在map-reduce任务结束时合并小文件
SET hive.merge.size.per.task = 268435456; -- 默认256M
SET hive.merge.smallfiles.avgsize = 16777216; -- 当输出文件的平均大小小于16m该值时,启动一个独立的map-reduce任务进行文件merge
(3)开启JVM重用
set mapreduce.job.jvm.numtasks=10
9)开启map端combiner(不影响最终业务逻辑)
set hive.map.aggr=true;
10)压缩(选择快的)
设置map端输出、中间结果压缩。(不完全是解决数据倾斜的问题,但是减少了IO读写和网络传输,能提高很多效率)
set hive.exec.compress.intermediate=true --启用中间数据压缩
set mapreduce.map.output.compress=true --启用最终数据压缩
set mapreduce.map.outout.compress.codec=…; --设置压缩方式
11)采用tez引擎或者spark引擎
12-Hive解决数据倾斜方法
2)怎么产生的数据倾斜?
(1)不同数据类型关联产生数据倾斜
情形:比如用户表中user_id字段为int,log表中user_id字段既有string类型也有int类型。当按照user_id进行两个表的Join操作时。
后果:处理此特殊值的reduce耗时;只有一个reduce任务默认的Hash操作会按int型的id来进行分配,这样会导致所有string类型id的记录都分配到一个Reducer中。
解决方式:把数字类型转换成字符串类型
select from users a
left outer join logs b
on a.usr_id = cast(b.user_id as string)
(2)控制空值分布
在生产环境经常会用大量空值数据进入到一个reduce中去,导致数据倾斜。
解决办法:
自定义分区,将为空的key转变为字符串加随机数或纯随机数,将因空值而造成倾斜的数据分不到多个Reducer。
注意:对于异常值如果不需要的话,最好是提前在where条件里过滤掉,这样可以使计算量大大减少
3)解决数据倾斜的方法?
(1)group by
注:group by 优于distinct group
解决方式:采用sum() group by的方式来替换count(distinct)完成计算。
(2)mapjoin
(3)开启数据倾斜时负载均衡
set hive.groupby.skewindata=true;
思想:就是先随机分发并处理,再按照key group by来分发处理。
操作:当选项设定为true,生成的查询计划会有两个MRJob。
第一个MRJob中,Map的输出结果集合会随机分布到Reduce中,每个Reduce做部分聚合操作,并输出结果,这样处理的结果是相同的GroupBy Key有可能被分发到不同的Reduce中,从而达到负载均衡的目的;
第二个MRJob再根据预处理的数据结果按照GroupBy Key分布到Reduce中(这个过程可以保证相同的原始GroupBy Key被分布到同一个Reduce中),最后完成最终的聚合操作。
点评:它使计算变成了两个mapreduce,先在第一个中在shuffle过程partition时随机给 key打标记,使每个key随机均匀分布到各个reduce上计算,但是这样只能完成部分计算,因为相同key没有分配到相同reduce上。
所以需要第二次的mapreduce,这次就回归正常shuffle,但是数据分布不均匀的问题在第一次mapreduce已经有了很大的改善,因此基本解决数据倾斜。因为大量计算已经在第一次mr中随机分布到各个节点完成。
13-Hive 动态分区
分区是hive存放数据的一种方式。将列值作为目录来存放数据,就是一个分区。这样查询时使用分区列进行过滤,只需根据列值直接扫描对应目录下的数据,不扫描其他不关心的分区,快速定位,提高查询效率。hive中支持两种类型的分区:
静态分区SP(static partition)动态分区DP(dynamic partition)
静态分区与动态分区的主要区别在于静态分区是手动指定,而动态分区是通过数据来进行判断。详细来说,静态分区的列是在编译时期,通过用户传递来决定的;
动态分区只有在SQL执行时才能决定,对分区表Insert数据时候,数据库自动会根据分区字段的值,将数据插入到相应的分区中,可以根据查询得到的数据自动匹配到相应的分区中去。只不过,使用Hive的动态分区,需要进行相应的配置。
set hive.exec.dynamic.partition=true;–是否允许动态分区
set hive.exec.dynamic.partition.mode=nonstrict; --分区模式设置
14-Tez/MR/Spark引擎优点?
Tez可以将多个有依赖的作业转换为一个作业,这样只需写一次HDFS,且中间节点较少,从而大大提升作业的计算性能。
Mr/tez/spark区别:
Mr引擎:多job串联,基于磁盘,落盘的地方比较多。虽然慢,但一定能跑出结果。一般处理,周、月、年指标。
Spark引擎:虽然在Shuffle过程中也落盘,但是并不是所有算子都需要Shuffle,尤其是多算子过程,中间过程不落盘 DAG有向无环图。 兼顾了可靠性和效率。一般处理天指标。
Tez引擎:完全基于内存。 注意:如果数据量特别大,慎重使用。容易OOM。一般用于快速出结果,数据量比较小的场景。
15-Union与Union all区别
1)union会将联合的结果集去重,效率较union all差
2)union all不会对结果集去重,所以效率高
16-HQL的任务执行流程&执行顺序
from ..on .. join .. where .. group by .. having .. select .. distinct .. order by .. limit
Hive直接访问存储在 HDFS 中或者 HBase 中的文件,通过 MapReduce、Spark 或 Tez 执行查询。
在 Hive 这一侧,总共有五个组件
UI:用户界面。可看作我们提交SQL语句的命令行界面。
DRIVER:驱动程序。接收查询的组件。该组件实现了会话句柄的概念。
COMPILER:编译器。负责将 SQL 转化为平台可执行的执行计划。对不同的查询块和查询表达式进行语义分析,并最终借助表和从 metastore 查找的分区元数据来生成执行计划。
METASTORE:元数据库。存储 Hive 中各种表和分区的所有结构信息。
EXECUTION ENGINE:执行引擎。负责提交 COMPILER 阶段编译好的执行计划到不同的平台上。
上图的基本流程是:
步骤1:UI 调用 DRIVER 的接口;
步骤2:DRIVER 为查询创建会话句柄,并将查询发送到 COMPILER(编译器)生成执行计划;
步骤3和4:编译器从元数据存储中获取本次查询所需要的元数据,该元数据用于对查询树中的表达式进行类型检查,以及基于查询谓词修建分区;
步骤5:编译器生成的计划是分阶段的DAG,每个阶段要么是 map/reduce 作业,要么是一个元数据或者HDFS上的操作。将生成的计划发给 DRIVER。
如果是 map/reduce 作业,该计划包括 map operator trees 和一个 reduce operator tree,执行引擎将会把这些作业发送给 MapReduce :
步骤6、6.1、6.2和6.3:执行引擎将这些阶段提交给适当的组件。在每个 task(mapper/reducer) 中,从HDFS文件中读取与表或中间输出相关联的数据,并通过相关算子树传递这些数据。最终这些数据通过序列化器写入到一个临时HDFS文件中(如果不需要 reduce 阶段,则在 map 中操作)。临时文件用于向计划中后面的 map/reduce 阶段提供数据。
步骤7、8和9:最终的临时文件将移动到表的位置,确保不读取脏数据(文件重命名在HDFS中是原子操作)。对于用户的查询,临时文件的内容由执行引擎直接从HDFS读取,然后通过Driver发送到UI。
17-连接与查询 inner join 与 left join 之间的区别
left join(左联接) 产生表A的完全集,而表B中匹配的则有值,没有匹配的则以null值取代.RIGHT(OUTER) JOIN 产生表B的完全集,而表A中匹配的则有值,没有匹配的则以null值取代INNER JOIN 产生的结果是AB的交集
FULL (OUTER) JOIN 产生A和B的并集,对于没有匹配的记录,以null值做为值
18-Hive 存储格式
- Textfile行式存储,这是hive表的默认存储格式,默认不做数据压缩,磁盘开销大,数据解析开销大,数据不支持分片(即代表着会带来无法对数据进行并行操作)
- Orc行列式存储,将数据按行分块,每个块按列存储,其中每个块都存储着一个索引,支持none和zlib和snappy这3种压缩方式,默认采用zlib压缩方式,不支持切片,orc存储格式能提高hive表的读取写入和处理的性能。ORC文件也是以二进制方式存储的,所以是不可以直接读取,ORC文件也是自解析的,它包含许多的元数据,这些元数据都是同构ProtoBuffer进行序列化的
- Parquet列式存储,是一个面向列的二进制文件格式(不可直接读取),文件中包含数据和元数据,所以该存储格式是自解析的,在大型查询时效率很快高效,parquet主要用在存储多层嵌套式数据上提供良好的性能支持,默认采用uncompressed不压缩方式。
parquet优点:
- 可以提升其查询性能,查询的时候不需要扫描全部的数据,而只需要读取每次查询涉及的列,这样可以将I/O消耗降低N倍,另外可以保存每一列的统计信息(min、max、sum等),实现部分的谓词下推。
- 由于每一列的成员都是同构的,可以针对不同的数据类型使用更高效的数据压缩算法,进一步减小I/O。
- 由于每一列的成员的同构性,可以使用更加适合CPU pipeline的编码方式,减小 CPU 的缓存失效。
orc和parquet的区别:
19-Hive压缩格式
hive主要支持gzip、zlib、snappy、lzo 这四种压缩方式。压缩不会改变元数据的分割性,即压缩后原来的值不变。
- 压缩率的话:gzip压缩率最佳,但压缩解压缩速度较慢
- 压缩速度的话:snappy压缩解压缩速度最佳,但压缩率较低
- 是否可切片的话:gzip/snappy/zlib是不支持切片,而lzo支持切片
20-分区和分桶的区别
分区:分区表是指按照数据表的某列或某些列分为多个区,区从形式上可以理解为文件夹。
分桶:相对分区进行更细粒度的划分。指定分桶表的某一列,让该列数据按照哈希取模的方式随机、均匀地分发到各个桶文件中。因为分桶操作需要根据某一列具体数据来进行哈希取模操作,故指定的分桶列必须基于表中的某一列(字段)。因为分桶改变了数据的存储方式,它会把哈希取模相同或者在某一区间的数据行放在同一个桶文件中。如此一来便可提高查询效率,如:我们要对两张在同一列上进行了分桶操作的表进行 JOIN 操作的时候,只需要对保存相同列值的桶进行JOIN操作即可。
区别:
(1)从表现形式上:分区表是一个目录,分桶表是文件
(2)从创建语句上:分区表使用partitioned by 子句指定,以指定字段为伪列,需要指定字段类型分桶表由clustered by 子句指定,指定字段为真实字段,需要指定桶的个数
(3)从数量上:分区表的分区个数可以增长,分桶表一旦指定,不能再增长
(4)从作用上:分区避免全表扫描,根据分区列查询指定目录提高查询速度分桶保存分桶查询结果的分桶结构(数据已经按照分桶字段进行了hash散列)。分桶表数据进行抽样和JOIN时可以提高MR程序效率
21-两个大表 join 如何优化
分桶表 + map join。
对两张在同一列上进行了分桶操作的表进行 JOIN 操作的时候,只需要对保存相同列值的桶进行 JOIN 操作即可。
22-Hive常用调优参数
小表大表Join(MapJoin)。
开启 Map 端聚合参数设置,防止数据倾斜。
一般 COUNT DISTINCT 使用先 GROUP BY 再 COUNT 的方式替换。
尽量避免笛卡尔积。
合理设置 Map 及 Reduce 数。
小文件进行合并。
严格模式:where 语句中含有分区字段过滤条件来限制范围,否则不允许执行。
JVM 重用。
压缩。
set hive.exec.parallel=true;
打开任务并行执行
hive.exec.dynamic.partition=true;
开启动态分区功能
hive.exec.dynamic.partition.mode=nostrict
允许所有分区都是动态的
set hive.map.aggr=true;
默认值是true,当选项设定为true时,开启map端部分聚合set hive.groupby.skewindata = ture;
默认值是false,当有数据倾斜的时候进行负载均衡
set hive.exec.parallel=true;
打开任务并行执行set hive.exec.parallel.thread.number=16;
同一个sql允许最大并行度,默认值为8。
23-引起Shuffle的sql操作
count distinct/group by/join on/union
24-Hive和Spark的区别
1、spark是一个计算引擎,而hive是一个存储框架、查询引擎。
2、HDFS上的数据其实都是文件,没有良好的组织形式。Hive将这些数据规整起来,让用户可以以结构化数据库的角度去使用这些数据(建立了HDFS数据的元数据信息),在Hive上写HQL操作表翻译成了MapReduce去读HDFS的数据进行计算。而Spark只是一个计算引擎,不是存储引擎,其本身并不负责数据存储,只能从外部数据存储获取数据,比如Hive。
3、Hive只能通过HiveQL操作数据,Spark可以通过SQL以及工程任务数据分析,支持JAVA、SCALA等开发语言
4、Hive是基于Hadoop的数据仓库,Saprk是基于内存执行高效的数据分析以及流数据处理
#数据人的面试交流地##阿里巴巴##面试#