大数据常见面试题之hive

一.描述一下Hive动态分区和分桶使用场景和使用方法

1.分区

  • 按照数据表的某列或某些列分为多个分区,分区从形式上可以理解为文件夹,比如我们要收集某个大型网站的日志数据,一个网站每天的日志数据存在同一张表上,由于每天会生成大量的日志,导致数据表的内容巨大,在查询时进行全表扫描耗费的资源非常多。那其实这个情况下,我们可以按照日期对数据进行分区,不同日期的数据存放在不同的分区,在查询时只要指定分区字段的值就可以直接从该分区查找。分区是以字段的形式在表结构中存在,通过describe table命令可以查看字段存在,但是该字段不存放实际的数据内容,仅仅是分区的表示(伪列)。

1. 静态分区

create table if not exists sopdm.wyp2(id int,name string,tel string)
partitioned by(age int) row format delimited fields terminated by,’ stored as textfile;
-- overwrite是覆盖,into是追加
insert into table sopdm.wyp2 partition(age=25) select id,name.tel from sopdm.wyp;

2. 动态分区

-- 设置为true表示开启动态分区功能(默认为false)
set hive.exec.dynamic.partition=true;
-- 设置为nonstrict,表示允许所有分区都是动态的(默认为strict)
set hive.exec.dynamic.partition.mode=nonstrict;
-- insert overwrite是覆盖,insert into是追加
insert overwrite table sopdm.wyp2 partition(age) select id,name.tel,age from sopdm.wyp;

3. 静态分区和动态分区的区别

  • 静态分区与动态分区的主要区别在于静态分区是手动指定,而动态分区是通过数据来进行判断。详细来说:
  • 静态分区:
    • 静态分区是在编译期间指定的指定分区名。
    • 支持load和insert两种插入方式。
    • 适用于分区数少,分区名可以明确的数据。
  • 动态分区:
    • 根据分区字段的实际值,动态进行分区。
    • 是在sql执行的时候进行分区。
    • 需要先将动态分区设置打开。set hive.exec.dynamic.partition.mode=nonstrict
    • 只能用insert方式。
    • 通过普通表选出的字段包含分区字段,分区字段放置在最后,多个分区字段按照分区顺序放置。

2.分桶

  • 分桶是相对分区进行更细粒度的划分。分桶将整个数据内容安装某列属性值得hash值进行区分,如果按照name属性分为3个桶,就是对name属性值的hash值对3取模,按照取模结果对数据分桶。如取模结果为0的数据记录存放到一个文件,取模为1的数据存放到一个文件,取模为2的数据存放到一个文件。
CREATE TABLE bucketed_user(id INT) name STRING CLUSTERED BY (id) INTO 4 BUCKETS;
  • 对于每一个表(table)或者分区,可以进一步组织成桶,也就是说桶是更为细粒度的数据范围划分。Hive也是针对某一列进行桶的组织。Hive采用对列值哈希,然后除以桶的个数求余的方式决定该条记录存放在哪个桶当中。把表(或者分区)组织成桶(Bucket)有两个理由:
  • 1)获得更高的查询处理效率
  • 桶为表加上了额外的结构,Hive在处理有些查询时能利用这个结构。具体而言,连接两个在(包含连接列的)相同列上划分了桶的表,可以使用Map端连接(Map-side join)高效的实现。比如JOIN操作。对于JOIN操作两个表有一个相同的列,如果对这两个表都进行了桶操作。那么将保存相同列值的桶进行JOIN操作就可以,可以大大减少JOIN的数据量。
  • 2)使取样(sampling)更高效
    在处理大规模数据集时,在开发和修改查询的阶段,如果能在数据集的一小部分数据上试运行查询,会带来很多方便。

二.Hive是怎么集成HBase

  • 1)首先我们需要将HBase的客户端jar拷入Hive lib目录下。
  • 2)修改hive/conf下的hive-site.xml配置文件,在最后添加如下属性。
<property>
                <name>hbase.zookeeper.quorum</name>
                <value>hadoop01</value>
 </property>

注意 CDH版本不需要额外操作,相同CDH版本下的软件,已经做过整合。

  • 3)启动Hive,创建表管理表hbase_table_1,指定数据存储在HBase表中。
create table hbase_table_1
(... ) -- 对应列
stored by ‘org.apache.hadoop.hive.hbase.HBaseStorageHandler’
with serdeproperties
(“hbase.columns.mapping”=":key,accuracy:total_score,accuracy:question_count,accuracy:accuracy")
tblproperties(“hbase.table.name”=“exam:analysis”);
  • 4)往Hive表hbase_table_1表中插入数据。

三.Hive join查询的时候on和where有什么区别

左右关联时:
• 条件不为主表条件时,放在on和where后面一样。
• 条件为主表条件时,放在on后面,结果为主表全量,放在where后面为主表条件筛选过后的全量。

-- 1.	select * from a left join b on a.id=b.id and a.dt=20181115;
-- 2.	select * from a left join b on a.id=b.id and b.dt=20181115;
-- 3.	select * from a join b on a.id=b.id and a.dt=20181115;
-- 4.	select * from a left join b on a.id=b.id where a.dt=20181115;
-- sql1: 如果是left join 在on 上写主表a的条件不会生效,全表扫描。
-- sql2: 如果是left join 在 on 上写副表b的条件会生效,但是语义与写到where条件不同。
-- sql3: 如果是inner join 在on 上写主表a,副表b的条件都会生效。
-- sql4: 建议这么写,大家写sql 大部分的语义都是先过滤数据然后再join,所以在不了解 join on +条件的情况下,条件尽量别写到on后,直接写到where后就ok了。

四.Hive里面的left join是怎么执行的?

  • 不考虑where条件下,left join会把左表所有数据查询出来,on及其后面的条件仅仅会影响右表的数据(符合就显示,不符合全部为null)。
  • 在join阶段。where字句的条件都不会被使用,仅在join阶段完成以后,where子句条件才会被使用,它将从匹配阶段产生的数据中检索过滤。
  • 所以左连接关注的是左边的主表数据,不应该把on后面的从表中的条件加到where后,这样会影响原有主表的数据。
  • where后面:是先连接生成临时查询结果,然后再筛选on后面:先根据条件过滤筛选,再连接生成临时查询结果。
  • 对于条件在on加个and还是用子查询。查询结果是一模一样的,至于如何使用这个需要分情况,用子查询的话会多一个maptask,但是如果利用这个子查询能过滤很多数据的话,用子查询还是比较建议的,因为不会加载太多的数据到内存中,如果过滤数据不多的情况下,建议用on后面加and条件。

五.Hive内部表,外部表,分区表

1.内部表
• 与数据库中的Table在概念上是类似的。
• 每一个Table在Hive中都有一个相应的目录存储数据。
• 所有的Table数据(不包括 External Table)都保存在这个目录中。
• 删除表时,元数据与数据都会被删除。
2.外部表
• 指向已经在HDFS中存在的数据,可以创建Partition。
• 它和内部表在元数据的组织上是相同的,而实际数据的存储则有较大的差异。
• 外部表只有一个过程,加载数据和创建表同时完成,并不会移动到数据库目录中,只是与外部数据建立一个连接,当删除一个外部表时,仅删除连接和元数据。
3.分区表
• Partition 对应于数据库的Partition列的密集索引。
• 在Hive中,表中的一个Partition对应于表下的一个目录,所有的Partition的数据都存储在对应的目录中。

六.Hive和mysql有什么区别,大数据为什么不用MySQL做存储和数据处理?

  • Hive是基于Hadoop构建的一套数据仓库分析系统,它提供了丰富的SQL查询方式来分析存储在Hadoop分布式文件系统中的数据;可以将结构化的数据文件映射为一张数据库表,并提供完整的SQL查询查询功能;可以将SQL语句转换为MapReduce任务运行,通过自己的SQL查询分析需要的内容,这套SQL简称Hive SQL,使不熟悉MapReduce的用户可以很方便地利用SQL语言查询,汇总和分析数据。而MapReduce开发人员可以把自己写的mapper和reduce作为插件来支持Hive做更复杂的数据分析。
  • 它与关系型数据库的SQL略有不同,但支持了绝大多数的语句如DDL,DML以及常见的聚合函数,连接查询,条件查询。它还提供了一系列的工具进行数据提取转化加载,用来存储,查询和分析存储在Hadoop中的大规模数据集。并支持UDF(User-Defined Function),UDAF(User-Defined AggregateFunction)和UDTF(User-Defined Table-Generating Function),也可以实现对map和reduce函数的定制,为数据操作提供了良好的伸缩性和可扩展性。
  • Hive不适合用于联机(online)事务处理,也不提供实时查询功能。它最适合应用在基于大量不可变数据的批处理作业。Hive的特点包括:可伸缩(在Hadoop的集群上动态添加设备),可扩展。容错,输入格式的松散耦合。
  • Hive构建在基于静态批处理的Hadoop之上,Hadoop通常都有较高的延迟并且在作业提交和调度的时候需要大量的开销。因此,Hive并不能够在大规模数据集上实现低延迟快速的查询,例如,Hive在几百MB的数据集上执行查询一般有几分钟级的时间延迟。
  • 因此,Hive并不适合那些需要高实性的应用,例如,联机事务处理(OLTP)。Hive查询操作过程严格遵守Hadoop MapReduce的作业执行模型,Hive将用户的HiveQL语句通过解释器转换为MapReduce作业提交到Hadoop集群上,Hadoop监控作业执行过程,然后返回作业执行结果给用户。Hive并非为联机事务处理而设计,Hive并不提供实时的查询和基于行级的数据更新操作。Hive的最佳使用场合是大数据集的批处理作业,例如,网络日志分析。

总结如下:
• 查询语言不同:Hive是hql语言,mysql是sql语句;
• 数据存储位置不同:Hive是把数据存储在HDFS上,而mysql数据是存储在自己的系统中;
• 数据格式:Hive数据格式可以用户自定义,mysql有自己的系统定义格式;
• 延迟性:Hive延迟性高,而mysql延迟性低
• 数据规模:Hive存储的数据量超级大,而mysql只是存储一些少量的业务数据;
• 底层执行原理:Hive底层是用的MapReduce,而mysql是excutor执行器。

七.Hive如何调优

  • 1)设置本地模式。
  • 2)并行执行。
  • 3)JVM重用。
  • 4)严格模式。
  • 5)合理设置map和reduce的数量。
  • 6)Fetch抓取。
  • 7)防止数据倾斜参数的开启,会生成两个MR Job。
  • 8)explain执行计划,通过执行计划来调节SQL语句。

八.Hive数据倾斜原因和处理?

1.产生原因

  • 1)key分布不均匀。
  • 2)业务数据本身的特性。
  • 3)建表时考虑不周。
  • 4)某些SQL语句本身就有数据倾斜,例如:
    • 大表join小表,其中小表key集中,分发到某一个或几个Reduce上的数据远高平均值。
    • 大表join小表,但是分桶的判断字段0值或空值过多,这些空值都由一个reduce处理,非常慢。
    • group by,group by维度过小,某值的数量过多,处理某值的reduce非常耗时
    • Count Distinct,某特殊值过多,处理此特殊值的reduce耗时。

2.解决方案

[参数调节]
hive.map.aggr=true
  • Map端部分聚合,相当于Combinerhive.groupby.skewindata=true
  • 有数据倾斜的时候进行负载均衡,当选项设定为true,生成的查询计划会有两个MRJob。第一个MRJob中,Map的输出结果集合会随机分布到Reduce中,每个Reduce做部分聚合操作,并输出结果,这样处理的结果是相同的Group By Key有可能被分发到不同的Reduce中,从而达到负载均衡的目的;第二个MR Job再根据预处理的数据结果按照Group By Key分布到Reduce中(这个过程可以保证相同的Group By Key被分布到同一个Reduce中),最后完成最终的聚合操作。
[SQL调整]

• 如何Join:关于驱动表的选取,选用join key分布最均匀的表作为驱动表,做好列裁剪和filter操作,以达到两表做join的时候,数据量相对变小的效果。
• 大小表Join:使用map join让小的维度表(1000条以下的记录条数)先进内存。在map端完成reduce。
• 大表Join大表:把空值的key变成一个字符串加上随机数,把倾斜的数据分到不同的reduce上,由于null值关联不上,处理后并不影响最终结果。
• count distinct大量相同特殊值,将值为空的情况单独处理,如果是计算count distinct,可以不用处理,直接过滤,在最后结果中1。如果还有其他计算,需要进行group by,可以先将值为空的记录单独处理,再和其他计算结果进行union。
• group by维度过小:采用sum() group by的方式来替换count(distinct)完成计算。
• 特殊情况特殊处理:在业务逻辑优化效果的不大情况下,有些时候是可以将倾斜的数据单独拿出来处理。最后union回去。

九.Hive的自定义函数实现了什么接口什么方法?

  • Hive自定义函数包括三种 UDF,UDAF,UDTF:
  • 1)UDF(User-Defined-Function)一进一出,继承了org.apache.hadoop.hive.ql.exec.UDF类,并覆写了evaluate方法。
  • 2)UDAF(User-Defined Aggregation Funcation) 聚合函数,多进一出。如 Count/max/min
  • 3)UDTF(User-Defined Table-Generating Functions)一进多出,如lateral view explore

十.使用Hive-sql如何查询A表中B表不存在的数据?

select distinct A.ID from A where A.ID not in(select ID from B)
select * from A where(select count(1)as num from A where A.ID=B.ID)=0

十一.如何控制Hive中Mapper和Reduce的个数

1.控制Hive任务中的map数

  • 通常情况下,作业会通过input的目录产生一个或者多个map任务。
  • 主要的决定因素有:input的文件总个数,input的文件大小,集群设置的文件块大小(目前为128M,可在Hive中通过set dfs.block.size;命令查看到,该参数不能自定义修改)
  • 举例:假设input目录下有1个文件a,大小为780M,那么Hadoop会将该文件a分隔成7个块(6个128m的块和一个12m的块),从而产生7个map数假设input目录下有3个文件a,b,c,大小分别为10m,20m,130m,那么Hadoop会分隔成4个块(10M,20M,128M,2M),从而产生4个map数,即,如果文件大于块大小(128M),那么会拆分,如果小于块大小,则把该文件当成一个块。

问题1:是不是map数越多越好?

  • 答案是否定的。如果一个任务有很多小文件(远远小于块大小128M),则每个文件也会被当做一个块,用一个map任务来完成,而一个map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的map数是受限的。

问题2:是不是保证每个map处理接近128M的文件块,就高枕无忧了?

  • 答案也是不一定。比如有一个127M的文件,正常会用一个map去完成,但这个文件只有一个或者两个小字段,却有几千万的记录,如果map处理的逻辑比较复杂,用一个map任务去做,肯定也比较耗时。

针对上面的两个问题,我们需要采用两种方式来解决:即减少map数和增加map数。

  • 1)如何合并小文件,减少map数?
  • 假设一个SQL任务:select count(1) from popt_tbaccountcopy_mes where pt=‘2012-07-04’;
  • 该任务的 inputdir /group/p_sdo_data/p_sdo_data_etl/pt/popt_tbaccountcopy_mes/pt=2012-07-04共有194个文件,其中很多是远远小于128M的小文件,总大小9G,正常执行会用194个map任务。Map总共消耗的计算资源:SLOTS_MILLIS_MAPS=623,020我通过以下方法来在map执行前合并小文件,减少map数:
set mapred.max.split.size=100000000;
set mapred.min.split.size.per.node=100000000;
set mapred.min.split.size.per.rack=100000000;
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
  • 再执行上面的语句,用了74个map任务,map消耗的计算资源:SLOTS_MILLIS_MAPS=333,500,对于这个简单SQL任务,执行时间上可能差不多,但节省了一半的计算资源。大概解释一下,100000000表示100M,set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;这个参数表示执行前进行小文件合并,前面三个参数确定合并文件块的大小,大于文件块大小128M的,按照128M来分隔,小于128M,大于100M的。按照100M来分隔,把那些小于100M的(包括小文件和分隔大文件剩下的),进行合并,最终生成了74个块。
  • 2)如何适当的增加map数?
  • 当input的文件都很大,任务逻辑复杂,map执行非常慢的时候,可以考虑增加Map数,来使得每个map处理的数据量减少,从而提高任务的执行效率。假设有这样一个任务:
select data_desc,
count(1),
count(distinct id),
sum(case when),
sum(case when),
sum()
from a group by data_desc
  • 如果表a只有一个文件,大小为120M,但包含几千万的记录,如果用一个map去完成这个任务,肯定是比较耗时的,这种情况下,我们要考虑将这一个文件合理的拆分成多个,这样就可以用多个map任务去完成。
set mapred.reduce.task=10;
create table a_1 as
select * from a
distribute by rand(123);
  • 这样会将a表的记录,随机的分散到包含10个文件的a_1表中,再用a_1代替上面sql中的a表,则会用10个map任务去完成。每个map任务处理大于12M(几百万记录)的数据,效率肯定会好很多。
  • 看上去,貌似这两种有些矛盾,一个是要合并小文件,一个是要把大文件拆成小文件,这点正是重点需要关注的地方,根据实际情况,控制map数量需要遵循两个原则:使大数据量利用合适的map数;使单个map任务处理合适的数据量。

2.控制Hive任务的reduce数

  • Hive自己如何确定reduce数;reduce个数的设定极大影响任务执行效率,不指定reduce个数的情况下,Hive会猜测确定一个reduce个数,基于以下两个设定:
hive.exec.reducers.bytes.per.reducer(每个reduce任务处理的数据量,默认为1000^3=1G)
hive.exec.reducers.max(每个任务最大的reduce数,默认为999)
  • 计算reduce数的公式很简单,N=min(参数 2,总输入数据量/参数1)。即,如果reduce的输入(map的输出)总大小不超过1G,那么只会有一个reduce任务;如:
select pt,count(1) from popt_tbaccountcopy_mes where pt='2012-07-04group by pt;
-- /group/p_sdo_data/p_sdo_data_etl/pt/popt_tbaccountcopy_mes/pt=2012-07-04 总大小为9G多,因此这句话有10个reduce

调整reduce个数方法一:

  • 调整hive.exec.reducers.bytes.per.reducer参数的值;
set hive.exec.reduce.bytes.per.reducer=500000000;500M)
select pt,count(1) from popt_tbaccountcopy_mes where pt=2012-07-04group by pt;
-- 这次有20个reduce

调整reduce个数方法二:

set mapred.reduce.tasks=15;
select pt,count(1) from popt_tbaccountcopy_mes where pt=2012-07-04group by pt;
-- 这次有15个reduce
  • reduce个数并不是越多越好。
  • 同map一样,启动和初始化reduce也会消耗时间和资源;另外,有多少个reduce,就会有多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题;

什么情况下只有一个reduce?

  • 很多时候你会发现任务中不管数据量多大,不管你有没有设置调整reduce个数的参数,任务中一直都只有一个reduce任务;其实只有一个reduce任务的情况,除了数据量小于hive.exec.reducers.bytes.per.reducer参数值的情况外,还有以下原因:
  • 1)没有group by的汇总。
-- 比如把
select pt,count(1) from popt_tbaccountcopy_mes where pt=2012-07-04group by pt;
-- 写成
select pt,count(1) from popt_tbaccountcopy_mes where pt=2012-07-04;
-- 这点非常常见,希望大家尽量改写。
  • 2)Order by。
  • 3)有笛卡尔积。
  • 通常这些情况下,除了找办法来变通和避免,暂时没有什么好的办法,因为这些操作都是全局的,所以Hadoop不得不用一个reduce去完成;同样的,在设置reduce个数的时候也需要考虑这两个原则:使大数据量利用合适的reduce数;使单个reduce任务处理合适的数据量。

十二.请说明Hive中Sort By,Order By, Cluster By,Distrbute By的作用

1.order by

  • Hive中的order by跟传统的sql语言中的order by作用是一样的,会对查询的结果做一次全局排序,所以说,只有Hive的sql中制定了order by所有的数据都会到同一个reduce进行处理(不管有多少map,也不管文件有多少的block只会启动一个reduce)。但是对于大量数据这将会消耗很长的时间去执行。这里跟传统的sql还有一点区别:如果指定了hive.mapred.mode=strict(默认值是nonstrict),这时就必须指定limit来限制输出条数,原因是:所有的数据都会在同一个reduce端进行,数据量大的情况下可能不能出结果,那么在这样的严格模式下,必须指定输出的条数。

2.sort by

  • Hive中指定了sort by,那么在每个reducer端都会做排序,也就是说保证了局部有序(每个reducer出来的数据是有序的,但是不能保证所有的数据是有序的,除非只有一个reducer),好处是:执行了局部排序之后可以为接下来的全局排序提高不少的效率(其实就是做一次归并排序就可以做到全局排序了)

3.distribute by 和 sort by 一起使用

  • distribute by是控制map的输出在reducer是如何划分的,举个例子,我们有一张表,mid是指这个store所属的商户,money是这个商户的盈利,name是这个store的名字,执行Hive语句:select mid,money,name from store distribute by mid sort by mid asc,money asc。我们所有的mid相同的数据会被送到同一个reducer去处理,这就是因为指定了distribute by mid,这样的话就可以统计出每个商户中各个商店盈利的排序了(这个肯定是全局有序的,因为相同的商户会放到同一个reducer去处理)。这里需要注意的是distribute by必须要写在sort by之前。

4.cluster by

  • cluster by的功能就是distribute by 和sort by相结合,如下2个语句是等价的:
select mid,money,name from store cluster by mid;
select mid,money,name from store distribute by mid sort by mid;
  • 如果需要获得与3中语句一样得效果:
select mid,money,name from store cluster by mid sort by money

注意:被cluster by指定得列只能是降序,不能指定asc和desc。

十三.请说明Hive中split,coalesce以及collect list函数的用法?

  • 1)split将字符串转化为数组,即:split(‘a,b,c,d’,’,’) ==>[“a”,“b”,“c”,“d”]。
  • 2)coalesce(T v1,T v2,…)返回参数中的第一个非空值;如果所有值都为NULL,那么返回NULL。假如某个字段默认是null,你想其返回的不是null,而是比如0或其他值,可以使用这个函数
select coalesce(field_name,0) as value from table;
  • 3)collect_list将分组中的某列转化为一个数据返回,不去重(列转行函数)。
select collect_list(id) from table;
  • 4)collect_set也是将分组中的某列转化为一个数组返回,但是去重。

十四.简要描述数据库中的null,说出null在Hive底层如何存储,并解释select a.* from t1 a left outer join t2 b on a.id=b.id where b.id is null的含义

  • 1)null与任何值运算的结果都是null,可以使用is null,is not null函数指定其值为null情况下的取值。
  • 2)null 在hive底层默认是用‘\N’来存储的,可以通过alter table test SET SERDEPROPERTIES(‘serialization.null.format’=‘a’);来修改。
  • 3)查询出t1表中存在但是t2表中不存在的id相对应的t1表的所有字段。

十五.Hive有哪些保存元数据的方式,每个有什么特点

  • 内嵌模式。内嵌数据库derby,安装小,基于Java,JDBC和SQL标准。
  • 本地模式。MySql数据库,数据存储模式可以自己设置,持久化好,查看方便。
  • 远程模式。用于非Java客户端访问元数据库,在服务器端启动MetaStoreServer,客户端利用Thrift协议通过MetaStoreServer访问元数据库。它将Metastore分离出来,成为一个独立的Hive服务(Merastore服务还可以部署多个)。这样的模式可以将数据库层完全置于防火墙后,客户端就不再需要用户名和密码登录数据库,避免了认证信息的泄露。

十六.生产环境中为什么建议使用外部表?

  • 1)因为外部表不会加载数据到Hive,减少数据传输,数据还能共享。
  • 2)Hive不会修改数据,所以无需担心数据的损坏。
  • 3)删除表时,只删除表结构,不删除数据。

猜你喜欢

转载自blog.csdn.net/sun_0128/article/details/108565599