目录
- Hive 是什么
- 为什么要使用 Hive
- Hive 的优缺点
- Hive的实现逻辑,为什么处理小表延迟比较高
- 你可以说一下 HQL 转换为 MR 的任务流程吗 ***
- 你可以说一下 hive 的元数据保存在哪里吗 ***
- Hive与传统数据库之间的区别
- Hive内部表和外部表的区别 ***
- hive 动态分区与静态分区的区别
- 分桶表和分区表的区别 ***
- hive 的 join 底层实现
- Hive中大表和小表怎么聚合的?复制小表到map task的过程是什么样的?
- 大表和大表之间的 join 怎么避免内存溢出?
- Order By 和 Sort By 的区别
- 行转列和列转行函数
- 自定义过 UDF函数吗
- Hive中有很多重复的数据,你如何去重?
- count(distinct)会出现什么问题
- hive 小文件过多怎么办
- Hive 优化
- Hive的函数:UDF、UDAF、UDTF的区别
- 所有的Hive任务都会有MapReduce的执行吗
- Hive的文件存储格式都有哪些
- 你用过哪些窗口函数
- rank、dense_rank、row_number 的区别
- count(*)、count(列名)、count(1)有什么区别
- COUNT DISTINCT 和先 GROUP BY 再 COUNT
- union all 和 union 的区别
- Hive和spark的区别
- HQL 执行的很慢怎么定位到哪一句
- Hive 数据倾斜
Hive 是什么
Hive 是由 Facebook 开源用于解决海量结构化日志的数据统计工具。它是基于 Hadoop 的一个 数据仓库工具,可以将结构化的数据文件映射为一张表,并提供 类 SQL 查询功能。
其本质就是将 HQL 转化成 MapReduce 程序。
- Hive处理的数据存储在HDFS
- Hive分析数据底层的实现是MapReduce
- 执行程序运行在YARN上
为什么要使用 Hive
Hive是Hadoop生态系统中比不可少的一个工具,它提供了一种SQL(结构化查询语言)方言,可以查询存储在Hadoop分布式文件系统(HDFS)中的数据或其他和Hadoop集成的文件系统。
大多数数据仓库应用程序都是使用关系数据库进行实现的,并使用SQL作为查询语言。
Hive是一个构建在Hadoop之上的数据仓库工具,它提供了类似于SQL的查询语言——HiveQL,这使得那些熟悉关系型数据库和SQL的开发人员能够更容易地迁移到Hadoop生态系统上。Hive降低了将这些应用程序转移到Hadoop系统上的难度。凡是会使用SQL语言的开发人员都可以很轻松的学习并使用Hive。
如果没有Hive,那么这些用户就必须学习新的语言和工具,然后才能应用到生产环境中。另外,相比其他工具,Hive更便于开发人员将基于SQL的应用程序转移到Hadoop中。如果没有Hive,那么开发者将面临一个艰巨的挑战,如何将他们的SQL应用程序移植到Hadoop上。
Hive 的优缺点
(1)优点
- 操作接口采用类 SQL 语法,提供快速开发的能力(简单、容易上手)。
- 避免了去写 MapReduce,减少开发人员的学习成本。
- Hive 的执行延迟比较高,因此 Hive 常用于数据分析,对 实时性要求不高 的场合。
- Hive 的执行延迟比较高,因此其优势在于处理大数据,对于处理小数据没有优势。
- Hive 支持用户自定义函数,用户可以根据自己的需求来实现自己的函数。
(2)缺点
- Hive的HQL表达能力有限
- Hive自动生成的MapReduce作业,通常情况下不够智能化。
- 数据挖掘方面不擅长,由于MapReduce数据处理流程的限制,效率更高的算法却无法实现。
- Hive的效率比较低
- Hive的执行延迟比较高,因此Hive常用于数据分析,对实时性要求不高的场合。
- Hive调优比较困难,粒度较粗。
- Hive不支持实时查询和行级别更新
- Hive分析的数据是存储在HDFS上,HDFS不支持随机写,只支持追加写,所以在Hive中不能update和delete,能select和insert。
Hive的实现逻辑,为什么处理小表延迟比较高
因为其计算是通过MapReduce,MapReduce是批处理,高延迟的。 对于小表查询,MapReduce框架在启动任务、分配资源、执行任务以及回收资源等方面都存在一定的开销,这些开销可能比任务执行的时间还要长,这就会导致处理小表的延迟比较高。
你可以说一下 HQL 转换为 MR 的任务流程吗 ***
首先客户端提交 HQL 以后,hive 通过 解析器 将 SQL 转换成抽象语法树,然后通过 编译器 生成逻辑执行计划,再通过 优化器 进行优化,最后通过 执行器 转换为可以运行的物理计划,比如 MapReduce/spark,然后提交到 yarn 上执行。
你可以说一下 hive 的元数据保存在哪里吗 ***
默认是保存在 derby 数据库,但是这有一个缺点:derby 数据库不支持并发,也就是说不能同时两个客户端去操作 derby 数据库,因此通常情况下,都会配置 mysql 去存放元数据。
Hive与传统数据库之间的区别
我认为主要有三点的区别:
- 数据量,hive 支持大规模的数据计算,mysql 支持的小一些。
- 数据更新快不快,hive 官方是不建议对数据进行修改的,因为非常的慢,这一点我也测试过,而 mysql 经常会进行数据修改,速度也挺快的。
- 查询快不快,hive 大多数延迟都比较高的,mysql 会低一些,当然这也与数据规模有关,数据规模很大的时候,hive 不一定比 mysql 慢。
- 应用场景,数据库可以用在 Online 的应用中,但是Hive 是为数据仓库而设计的。
Hive内部表和外部表的区别 ***
从建表语句来看,未被 external 修饰的是内部表,被 external 修饰的就是外部表。
二者主要有两点区别
- 内部表的数据由 hive 自身管理,外部表的数据由 hdfs 管理。
- 删除内部表的时候,元数据和原始数据都会被删除,而删除外部表的时候仅仅会删除元数据,原始数据不会被删除。
使用场景:
- 内部表适合存储对数据的修改和删除操作,因为删除内部表时可以直接删除相关的数据文件。一般内部表在数仓中的DW(细节层)中使用。
- 外部表适合存储对数据的只读操作,比如将已经存在的数据文件导入到Hive中进行分析。一般外部表在数仓中的ODS层(贴源层)中使用
外部表相对来说更加安全些,数据组织也更加灵活,方便共享源数据。
在做统计分析时候用到的中间表,结果表可以使用内部表,因为这些数据不需要共享,使用内部表更为合适。
hive 动态分区与静态分区的区别
hive本身是没有索引的,查询数据时需要全表扫描,分区也就是分目录,可以减少全表扫描。
静态分区:
- 分区字段的值是在导入数据的时候手动指定的
动态分区
- 分区字段的值是基于查询结果自动推断出来的,也就是最后查询结果的最后一个字段值就对应分区字段的值
- 想使用动态分区表的时候必须要对 hive 进行两个配置
- 开启动态分区功能
hive.exec.dynamic.partition=true
- 设置动态分区的模式为非严格模式,也就是说允许所有分区字段都可以使用动态分区
hive.exec.dynamic.partition.mode-nonstrict
- 开启动态分区功能
分桶表和分区表的区别 ***
分桶表和分区表的作用都是用来减少全表扫描的。因为并非所有的数据都可以进行合理的分区,所以有了新的技术分桶表。分桶表的分桶规则是,根据分桶字段的hash 值,对桶的个数进行取 余运算,然后得到该数据应该放到哪个桶里面去。
(1)创建语句不同,分区表是 partitioned by,分桶表是 clustered by
(2)分区或分桶字段要求不同,分区字段不能是表中存在的字段,分桶字段一定是表中存在的字段。
(3)表现形式不同,分区表是其实就是分了目录存放数据,分桶表是将一个文件拆分为很多文件存放。
hive 的 join 底层实现
(1)Map Join
Map Join的实现方式有两种,分别是Bucket Map Join和Map Join。
- Bucket Map Join又叫桶映射连接,它是Map Join的一种实现方式,要求表在join列上进行了bucket操作,并且bucket数相同,这样在map端会将两张表中的同一个bucket同时读取进内存中,进行join操作。
- Map Join是将小表全部缓存到内存中,然后将大表的一条记录与小表在内存中的所有记录进行匹配,匹配成功后输出join结果。
- Map Join适用于小表驱动大表的情况,它可以极大地减少数据的读取量,加快join的速度。但是它的缺点是要求小表可以全部缓存到内存中。
(2)Reduce Join是指将两张表的数据都读入HDFS,然后在reduce端进行join操作。
Reduce Join分为Shuffle Join和Merge Join两种实现方式。
- Shuffle Join(又称普通的Reduce Join)是指先对两张表进行分区操作(Partition),然后在reduce阶段进行join操作。
- Shuffle Join的缺点是,因为需要将两张表的所有数据都拷贝到reduce端,所以在数据量较大的情况下,网络传输的开销很大,执行时间较长。
- Merge Join又叫排序合并连接,它是Reduce Join的一种实现方式。它首先对需要join的表按照join列进行排序,然后再按照 join 列将两张表join在一起。
- Merge Join的优点是不需要进行shuffle操作,执行效率较高。但是它的缺点是需要进行排序,如果需要join的表比较大,排序的时间和开销也比较大。
Map Join适用于小表驱动大表的情况,Reduce Join适用于大表驱动小表的情况,不同的实现方式在不同的情况下会有不同的优劣点。
Hive中大表和小表怎么聚合的?复制小表到map task的过程是什么样的?
当进行大表和小表的聚合操作时,Hive会根据优化器的规则自动选择合适的执行计划。一般来说,如果小表可以放在内存中,那么会先将小表复制到每个Map Task的内存中进行处理,然后再将结果传输回Reduce Task进行汇总,这个过程被称为Map Join。
复制小表到map task的过程,这个过程被称为Replication:
- 在Hive中,如果一个表被标记为小表(Small Table),那么它会被缓存在Hive服务器的本地内存中。
- 当执行包含小表和大表的Join操作时,Hive会启动MapReduce Job进行处理。
- 在Map任务执行之前,Hive会将小表从本地内存复制到每个Map任务所在的节点上。
- 在Map任务执行过程中,每个Map任务都可以直接读取本地节点上的小表数据,而不需要通过网络传输,从而大大加快了处理速度。
注意:只有当小表可以全部放入内存中时,才会使用 Map Join 来加速处理。如果小表的数据量太大,无法全部放入内存中,那么就必须使用普通的Reduce Join来处理,这种情况下,小表会和大表一样被划分为多个分片,分发到不同的Map任务上进行处理,最后再通过Reduce任务进行合并。
大表和大表之间的 join 怎么避免内存溢出?
(1)增加集群资源:通过增加Hadoop集群中的节点数或者增加每个节点的资源,例如内存、CPU等,来提高查询的性能和容错能力。
(2)使用Bucket Map Join:Bucket Map Join是一种将 join 操作划分为多个小任务的技术,可以减少每个任务的数据量,从而降低内存的消耗。在使用Bucket Map Join时,需要将表分桶,且分桶的列应该是 join 条件中的列。(要求表在join列上进行了bucket操作,并且bucket数相同,这样在map端会将两张表中的同一个bucket同时读取进内存中,进行join操作)
(3)使用Map Join:Map Join是一种将小表读入内存中的技术,可以减少内存的消耗。在使用Map Join时,需要将小表进行适当的压缩和优化,以提高查询的性能。
(4)使用索引:在Hive 3.0及以上版本中,可以使用ACID表的索引功能来优化查询,从而降低内存的消耗。通过对大表的索引,可以加速查询的速度,同时减少内存的使用。
Order By 和 Sort By 的区别
Order By:全局排序,只有一个 reducer,缺点: 当数据规模大的时候,就会需要很长的计算时间。
Sort By:分区排序,保证每个 reducer 内有序,一般结合 distribute by 来使用。
使用场景:在生产环境中,order by 用的比较少,容易导致 OOM;一般使用 distribute by+sort by
行转列和列转行函数
(1)行转列:一行转一列、多行转一列
CONCAT(string A/col, string B/col…):返回输入字符串连接后的结果,支持任意多个输入字符串。
CONCAT_WS(separator, str1, str2,…):它是一个特殊形式的 CONCAT()。第一个参数剩余参数间的分隔符。分隔符可以是与剩余参数一样的字符串。如果分隔符是NULL,返回值也将为NULL。这个函数会跳过分隔符参数后的任何NULL和空字符串。分隔符将被加到被连接的字符串之间。
COLLECT_SET(col):函数只接受基本数据类型,它的主要作用是将某字段的值进行去重汇总,产生array类型字段。
COLLECT_LIST(col):函数只接受基本数据类型,它的主要作用是将某字段的值进行不去重汇总,产生arr类型字段。
(2)列转行:
EXPLODE(col):将Hive一列中复杂的 array 或者 map 结构拆分成多行。(如果是array,结果只有一列,如果是map,结果只有key,value两列)
自定义过 UDF函数吗
(1)承UDF或者UDAF或者UDTF,实现特定的方法;
(2)打成jar包,上传到服务器
(3)执行命令 add jar路径
,目的是将 jar 包添加到 hive 中
(4)注册临时函数:create temporary function 函数名 as "自定义函数全类名"
(5)在select中使用 UDF 函数
Hive中有很多重复的数据,你如何去重?
(1)使用 DISTINCT关键字
可以在 SELECT 语句中选择不同的值,以去除重复数据。
例如:SELECT DISTINCT column_name FROM table_name;
(2)使用 GROUP BY
可以使用 GROUP BY 子句和聚合函数去掉重复数据。
例如:SELECT column_name, COUNT(*) FROM table_name GROUP BY column_name;
(3)使用窗口函数
可以使用窗口函数来进行去重。
需要注意的是,这种方法可能会影响性能,因为需要进行排序和分组操作,尤其是在处理大数据集时,可能会非常耗时。总的来说,对于数据去重,推荐使用 GROUP BY 的方式,执行效率较高。在需要处理复杂的去重逻辑时,可以考虑使用窗口函数,但需要注意性能问题。
count(distinct)会出现什么问题
我们写的是HQL,而它的底层引擎是MapReduce,是分布式计算的,所以自然会出现数据倾斜这种分布式计算的典型问题,就会导致运行时间过长。这里其实熟悉MapReduce原理的已经明白了这条sql跑的慢的原因,因为出现了很严重的数据倾斜,很多个mapper,却只有1个reducer,所有的数据在mapper处理过后全部只流向了一个reducer。
为什么只有一个reducer呢?因为使用了distinct和count(full aggreates),这两个函数产生的MapReduce作业只会产生一个reducer,而且哪怕显式指定set mapred.reduce.tasks=100000也是没用的。所以对于这种去重统计,如果在数据量够大,一般是一亿记录数以上(根据实际情况考虑),建议选择使用count加group by去进行统计,执行速度就会快很多。
总结:在数据量很大的情况下,使用count+group by替换count(distinct)能使作业执行效率和速度得到很大的提升,一般来说数据量越大提升效果越明显。
hive 小文件过多怎么办
hive 哪里会产生小文件
- 源数据本身有很多小文件
- 动态分区会产生大量小文件
- reduce个数越多,小文件越多
- 按分区插入数据的时候会产生大量的小文件, 文件个数 = maptask个数 * 分区数
- mapreduce过程中合并小文件:map后reduce前,map输出的时候设置小文件合并,ruduce输出的时候设置小文件合并。
- 直接设置少一点的 reduce。 mapreduce.job.reduces
- hadoop自带的解决小文件的方式:
- 在计算的时候,采用 CombineTextInputFormat 的切片方式,这样就可以将多个小文件放到一个切片中进行计算
- Sequence file 由一系列的二进制key/value组成,如果为key小文件名,value为文件内容,则可以将大批小文件合并成一个大文件。
- 采用 har 归档的方式对小文件进行存储,这样能够将多个小文件打包为一个har 文件
Hive 优化
(1)建表优化
- 分区表
- 分桶表
- 合适的文件格式
- 合适的压缩格式
(2)语法优化
- 列裁剪与分区裁剪
- Group By 优化
- 开启Map端聚合
- 开启负载均衡
- CBO 优化:选择代价最小的执行计划;自动优化 HOL 中多个 Join
的顺序,并选择合适的 Join 算法。 - 谓词下推:将 SOL 语句中的 where 谓词逻辑都尽可能提前执行
减少下游处理的数据量。 - MapJoin:将Join双方比较小的表直接分发到各个Map进程的内存中,在Map进程中进行Join操作,这样就不用进行Reduce步骤,从而提高了速度。
- SMB Join:分桶join,大表转换为很多小表,然后分别进行 join,最后 union 到一起。
(3)Job优化
- Hive Map优化
- 复杂文件增加Map数
- 在map执行前合并小文件,减少map数
- Map端聚合
- Hive Reduce优化
- 合理设置Reduce数
- 为什么 reduce 的数量不是越多越好?
- 过多的启动和初始化 reduce 也会消耗时间和资源;另外,有多少个 reduce,就会有多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题。
- Hive 任务整体优化
- 开启Fetch抓取。Hive中对某些情况的查询可以不必使用MapReduce计算。
hive.fetch.task.conversion=more
- 小数据集启用本地模式:hive.exec.mode.local.auto=true
- 多个阶段并行执行:set hive.exec.parallel-true
- JVM 重用:针对小文件过多的时候使用
- 开启Fetch抓取。Hive中对某些情况的查询可以不必使用MapReduce计算。
Hive的函数:UDF、UDAF、UDTF的区别
(1)UDF:单行进入,单行输出
(2)UDAF:多行进入,单行输出
(3)UDTF:单行输入,多行输出
所有的Hive任务都会有MapReduce的执行吗
不是,从 Hive 0.10.0 版本开始,对于简单的不需要聚合的类似 SELECT from LIMIT n
语句,不需要 MapReduce job,直接通过 Fetch task 获取数据。
Hive的文件存储格式都有哪些
Hive的文件存储格式有四种:TEXTFILE 、SEQUENCEFILE、ORC、PARQUET,前面两种是行式存储,后面两种是列式存储。如果为textfile的文件格式,直接load,不需要走MapReduce;如果是其他的类型就需要走MapReduce了,因为其他的类型都涉及到了文件的压缩,这需要借助MapReduce的压缩方式来实现。
(1)TEXTFILE:按行存储,不支持块压缩,默认格式,数据不做压缩,磁盘开销大,加载数据的速度最高;
(2)ORCFile:存储方式:数据按行分块,每块按照列存储压缩快,快速列存取效率比rcfile高,是rcfile的改良版本,使用了索引使用ORC文件格式可以提高hive读、写和处理数据的能力PARQUET:按列存储,相对于ORC,Parquet压缩比较低,查询效率较低
SequenceFile:Hadoop API提供的一种二进制文件,以<key,value>的形式序列化到文件中存储方式:行存储
总结压缩比:ORC > Parquet > textFile(textfile没有进行压缩)
查询速度:三者几乎一致
你用过哪些窗口函数
窗口函数:就是给聚合函数开操作的窗口(在明细查询中,展示汇总的结果)
rank、dense_rank、row_number
LAG(col,n,default_val):往前第n行数据。
LEAD(col,n, default_val):往后第n行数据。
FIRST_VALUE (col,true/false):当前窗口下的第一个值,第二个参数为true,跳过空值。
LAST_VALUE (col,true/false):当前窗口下的最后一个值,第二个参数为true,跳过空值。
NTILE(n):把有序窗口的行分发到指定数据的组中,各个组有编号,编号从1开始,对于每一行,NTILE返回此行所属的组的编号。
rank、dense_rank、row_number 的区别
在SQL中,ROW_NUMBER()、RANK()和DENSE_RANK()是用于排序的窗口函数,它们在生成排序结果时有一些区别:
- RANK():排序相同时会重复,总数不变,即会出现1、1、3这样的排序结果;
- DENSE_RANK():排序相同时会重复,总数会减少,即会出现1、1、2这样的排序结果;
- ROW_NUMBER()排序相同时不会重复,会根据顺序排序,即会出现1、2、3这样的排序结果;
count(*)、count(列名)、count(1)有什么区别
性能上:count(*)=count(1)>count(主键字段)>count(普通字段)
COUNT DISTINCT 和先 GROUP BY 再 COUNT
COUNT DISTINCT 和先 GROUP BY 再 COUNT 的操作在Hive中都用于去重计数。
COUNT DISTINCT 通常会将所有数据发送到一个Reduce任务进行去重计数,容易导致单一任务的负载过重,尤其是在数据量大且倾斜严重的情况下。
union all 和 union 的区别
union all:对两个结果集直接进行并集操作,记录可能有重复,不会进行排序。
union:对两个结果集进行并集操作,会进行去重,记录不会重复,按字段的默认规则排序。因此,从效率上说,union all 要比union更快。
Hive和spark的区别
即mapreduce与spark的区别
HQL 执行的很慢怎么定位到哪一句
开启慢查询日志,记录了在 mysql 中响应时间超过阈值的语句,这个时间默认是10s。
Hive 数据倾斜
如何解决数据倾斜?
- 开启map端聚合,相当于Combiner
- 使用随机数(重新设计key)
- 设置合理的map、reduce的task数
- SQL语句调节
- 大小表Join:使用map join让小的维度表(1000条以下的记录条数)先进内存。在map端完成reduce。
- 大表Join大表:把空值的key变成一个字符串加上随机数,把倾斜的数据分到不同的reduce上,由于null值关联不上,处理后并不影响最终结果。