hive面试问题总结,有些还没解答,欢迎补充

hive

1、hive数据倾斜问题,实际工作中如何处理的?

某些业务场景下,表关联的字段有大量的空值,这些空值在mapreduce任务中会按照一个key来处理全部拉取到一个分区,造成某个task处理的数据量过大,任务进度卡在这个task下。处理方式是,这些空值对应的数据也是业务需要的,将空值对应的数据单独过滤出来,对空值进行添加随机数拼接字符串处理,然后对跑批结果通过union 的方式跟其余正常数据合并。

1.1、空值数据倾斜

**场景:**如日志中,常会有信息丢失的问题,比如全网日志中的user_id,如果取其中的user_id和bmw_users关联,会碰到数据倾斜的问题。

解决方法1: user_id为空的不参与关联

Select * From log a
Join bmw_users b
On a.user_id is not null
And a.user_id = b.user_id
Union all
Select * from log a
where a.user_id is null;

**解决方法2 :**赋与空值分新的key值

Select *
from log a
left outer join bmw_users b
on case when a.user_id is null thenconcat(‘dp_hive’,rand() ) else a.user_id end = b.user_id; 

**结论:**方法2比方法效率更好,不但io少了,而且作业数也少了。方法1 log读取两次,jobs是2。方法2 job数是1 。这个优化适合无效id(比如-99,’’,null等)产生的倾斜问题。把空值的key变成一个字符串加上随机数,就能把倾斜的数据分到不同的reduce上 ,解决数据倾斜问题。附上hadoop通用关联的实现方法(关联通过二次排序实现的,关联的列为parition key,关联的列c1和表的tag组成排序的group
key,根据parition key分配reduce。同一reduce内根据group key排序)

1.2、不同数据类型关联产生数据倾斜

**场景:**一张表s8的日志,每个商品一条记录,要和商品表关联。但关联却碰到倾斜的问题。s8的日志中有字符串商品id,也有数字的商品id,类型是string的,但商品中的数字id是bigint的。猜测问题的原因是把s8的商品id转成数字id做hash来分配reduce,所以字符串id的s8日志,都到一个reduce上了,解决的方法验证了这个猜测。

**解决方法:**把数字类型转换成字符串类型

Select * from s8_log a
Left outer join r_auction_auctions b
On a.auction_id = cast(b.auction_id asstring);
1.3、Join的数据偏斜

MapReduce编程模型下开发代码需要考虑数据偏斜的问题,Hive代码也是一样。数据偏斜的原因包括以下两点:

\1. Map输出key数量极少,导致reduce端退化为单机作业。

\2. Map输出key分布不均,少量key对应大量value,导致reduce端单机瓶颈。

Hive中我们使用MapJoin解决数据偏斜的问题,即将其中的某个表(全量)分发到所有Map端进行Join,从而避免了reduce。这要求分发的表可以被全量载入内存。

极限情况下,Join两边的表都是大表,就无法使用MapJoin。

这种问题最为棘手,目前已知的解决思路有两种:

\1. 如果是上述情况1,考虑先对Join中的一个表去重,以此结果过滤无用信息。这样一般会将其中一个大表转化为小表,再使用MapJoin 。

一个实例是广告投放效果分析,例如将广告投放者信息表i中的信息填充到广告曝光日志表w中,使用投放者id关联。因为实际广告投放者数量很少(但是投放者信息表i很大),因此可以考虑先在w表中去重查询所有实际广告投放者id列表,以此Join过滤表i,这一结果必然是一个小表,就可以使用MapJoin。

\2. 如果是上述情况2,考虑切分Join中的一个表为多片,以便将切片全部载入内存,然后采用多次MapJoin得到结果。

一个实例是商品浏览日志分析,例如将商品信息表i中的信息填充到商品浏览日志表w中,使用商品id关联。但是某些热卖商品浏览量很大,造成数据偏斜。

例如,以下语句实现了一个inner join逻辑,将商品信息表拆分成2个表:

select * from
(
select w.id, w.time, w.amount, i1.name, i1.loc, i1.cat
from w left outer join i sampletable(1 out of 2 on id) i1
)
union all
(
select w.id, w.time, w.amount, i2.name, i2.loc, i2.cat
from w left outer join i sampletable(1 out of 2 on id) i2
)
);

以下语句实现了left outer join逻辑:

select t1.id, t1.time, t1.amount,
    coalease(t1.name, t2.name),
    coalease(t1.loc, t2.loc),
    coalease(t1.cat, t2.cat)
from (
    select w.id, w.time, w.amount, i1.name, i1.loc, i1.cat
    from w left outer join i sampletable(1 out of 2 on id) i1
) t1 left outer join i sampletable(2 out of 2 on id) t2;

上述语句使用Hive的sample table特性对表做切分。

2.hive自定义函数开发使用样例

Hive里面是有函数。在这里以一个流量信息统计的案例来说明函数体的使用。

源数据为:flow.txt

1389990045,http://www.163.com,20,200
1515566005,http://www.163.com,20,3009
1385566005,http://www.163.com,20,200
1389990045,http://www.163.com,2000,3900
1370876045,http://www.163.com,2000,5690

然后我们可以去eclipse 中建一个类,要继承org.apache.hadoop.hive.ql.exec.UDF类实现evaluate,然后打成jar包,然后将这个jar包上传至hive的classpath。

package cn.tf.hive; import java.util.HashMap; import org.apache.hadoop.hive.ql.exec.UDF; public class ConvertUDF extends UDF{ 	private static HashMap<String,String>  provinceMap=new HashMap<String,String>();
	static{
		provinceMap.put("137", "北京");
		provinceMap.put("138", "上海");
		provinceMap.put("139", "天津");
		provinceMap.put("151", "深圳");
	} 	public  String   evaluate(String phone){ 		String gsd=provinceMap.get(phone.substring(0,3));
		if(gsd==null){
			gsd="other";
		}
		return gsd;
	} 	public  long evaluate(long upflow,long downflow){ 		return (upflow+downflow);
	} }

自定义函数调用过程:

1.添加jar包(在hive命令行里面执行)

hive> add jar /home/admin1/hadoop/lx/udf.jar;

把源文件上传

hive>load data local inpath ‘/home/admin1/hadoop/lx/flow.txt’ into table t_flow;

2、创建临时函数:

hive>CREATE TEMPORARY FUNCTION convert AS 'cn.tf.hive.ConvertUDF';

3、建表:

hive> create table t_flow(phone string,url string,upflow int,downflow int)
    > row format delimited
    > fields terminated by ',';

4、执行函数查询结果

select phone,convert(phone),url,convert(upflow,downflow)  from t_flow;

img

5、保存查询出的结果

hive> create table t_flow_result
    > as
    > select phone,convert(phone),url,convert(upflow,downflow)  from t_flow;

img

img

3.hive自定义函数用来做什么?

4.hql运行时间不合理,怎么对其进行排查?

5.hive的锁表问题?

hive表锁只在涉及并发操作的时候才能遇到,
hive存在两种锁,共享锁Shared (S)和互斥锁Exclusive (X)
共享锁S 可以并发执行, 互斥锁X 不可以并发执行, 可具体到表的分区
直接在hadoop上 hadoop dfs -put 本地路径 hdfs路径 不触发锁。
可以用在shell上 执行 hadoop dfs -put 本地路径 hdfs路径 代替 load data,避免锁问题
load data 时若分区不存在会创建分区,而hadoop dfs -put不会, 需先调用alter table add partition来创建分区。
若一个操作正在读取表中数据,这时向表的分区中put数据,该数据在本次读时不会被加载,下次读操作时才会被加载
查看锁命令:

SHOW LOCKS <TABLE_NAME>;
SHOW LOCKS <TABLE_NAME> extended;
SHOW LOCKS <TABLE_NAME> PARTITION (<PARTITION_DESC>);
SHOW LOCKS <TABLE_NAME> PARTITION (<PARTITION_DESC>) extended;

8.Hive为什么不支持行级别的增删改?

Hive不支持行级别的增删改的根本原因在于他的底层HDFS本身不支持。在HDFS中如果对整个文件的某一段或某一行内容进行增删改,势必会影响整个文件在集群中的存放布局。需要对整个集群中的数据进行汇总,重新切块,重新发送数据到每个节点,并备份,这样的情况是得不偿失的。所以HDFS的设计模式使他天生不适合做这个事。
如果一个表要实现update和delete功能,该表就必须支持ACID,而支持ACID,就必须满足以下条件:
1、表的存储格式必须是ORC(STORED AS ORC);
2、表必须进行分桶(CLUSTERED BY (col_name, col_name, …) INTO num_buckets BUCKETS);
3、Table property中参数transactional必须设定为True(tblproperties(‘transactional’=‘true’));
4、以下配置项必须被设定:

Client端:
hive.support.concurrency – true
hive.enforce.bucketing – true
hive.exec.dynamic.partition.mode – nonstrict
hive.txn.manager – org.apache.hadoop.hive.ql.lockmgr.DbTxnManager
服务端:
hive.compactor.initiator.ontrue
hive.compactor.worker.threads – 1
hive.txn.manager – org.apache.hadoop.hive.ql.lockmgr.DbTxnManager(经过测试,服务端也需要设定该配置项)
-- 注意:上述配置项必须区分Client端和服务端。在Cloudera Manager上可以分别添加(hive—>配置—>高级),在更新部署配置信息的时候需要勾选部署客户端配置(默认是勾选的)。

9.实际工作中Hive怎么接入增量更新数据?

其主要的实现原理是将每天日增量数据抽取至临时表中,然后使用主键关联业务表获取其实际新增的数据,最后再和业务表进行 Union 得到当天的数据并存入当天的分区中。

使用窗口函数row_number进行实现

首先抽取的表必须要有一个唯一主键,先根据创建时间和修改时间将新增的和变化的数据拉去过来到一张临时表
将临时表和之前正式表进行union all操作
使用row_number函数根据唯一主键进行分组,根据修改时间进行排序,取出修改时间最大的那条数据,更新到正式表既可以(中间有个问题就是dt分区怎么处理),最后加载到正式表的数据是使用到动态分区的既可以解决dt分区问题。

10.hive更新和插入操作时,hdfs文件会出现怎样的变化

9.hive动态分区有哪些优缺点和坑

distribute by rand()建议使用带随机种子的函数 不然走mr发生重试的时候会有数据丢失风险

10.hive中的常用关键字(分区,分桶,指定路径。。。。。)

11.hive性能问题

(1)引发性能问题的原因
误区:1.数据量大(并不是)
真正因素:
1.数据冗余(笛卡尔积)
2.数据倾斜
3.io性能
(2)解决思路
调优:1.分区表(避免全表扫描,提高效率),经常使用的字段来作为分区表;
2.分桶表(圈定一个范围再去查找,加快join查询速度)两个表都是分桶表且个数保持一致;
3.存储格式(行式存储:textfile,列式存储:orc,parquet),建宽表用orc,orc查询速度会比较快;
4.压缩格式(通过压缩减少数据量,从而缩短io时间)压缩比率,解压速度,是否支持split(切割),所以使用lzo格式

12.不是所有的hql语句都转换为mr执行

例如:show databases;直接查询元数据库

13.hive的表和数据文件到底是怎么映射的?

数据仓库的默认路径:/uesr/hive/warehouse
库路径:/user/hive/warehouse/itheima.db
表路径:/user/hive/warehouse/itheima.db/t_archer
数据文件:/user/hive/warehouse/itheima.db/t_archer/archer.txt

14.hive的四种表(内部表,外部表,分区表,分桶表):

内部表:create table 表名();创建表的时候,默认把该表的数据 使用默认的组织方式,来存在默认的仓库路径
外部表:create external table 表名() row …location “/xxx/xxx/xx”
删除表的时候,内部表既删除元数据也删除数据文件:外部表删除元数据,不删除数据文件
分区表:
create table 表名(id int,name string) partitioned by (d_time timestamp)
注意:分区字段一定不能和表一样,但是在使用的时候,分区字段和表字段没有不同
没有做分区表之前,数据直接放在表的目录中,分区之后在表的目录中,再创建一些文件夹,每个文件夹包含了这个表的一部分数据,意味着:一个表的数据被分门别类的进行管理
注意:生产环境中,绝大部分的事实表都是分区表(大部分分区字段都是date类型,按天分区的概率是最大的)
分桶表:
是按照文件的形式来组织区分(分桶的规则:和默认的mapreduce的HashPartitioner一摸一样)
create table 表名(id int,name string,age int) clustered by(name) sort by (age) desc into 3 buckets;
这张表中的数据,按照name字段划分为三个文件来进行管理,每个文件中的数据会按照age降序排序


+注意:分区表:用来提高查询效率的。
+企业实践:若遇见大量sql中,按照某个字段进行过滤。那么在建表是,就应该按照这个字段作为分区字段进行建表:如果你的数据基本上,只会分析增量数据,不会分析全量数据,那么就应该按照时间进行分区。
+分桶表:也是用来提高查询效率的+用来进行抽样
+企业实践:如果一张表的某个字段经常被当作是一个join查询条件,就可以建分桶表进行优化。
+例如:select a.,b. from a jioin b a.id =b.id ; a表和b表都要分桶,而且分桶规则要一样。(分桶字段一致,分桶个数成倍数关系)


15.hive的外部表和内部表区别?

内部表:create table 表名();创建表的时候,默认把该表的数据 使用默认的组织方式,来存在默认的仓库路径
外部表:create external table 表名() row …location “/xxx/xxx/xx”
删除表的时候,内部表既删除元数据也删除数据文件:外部表删除元数据,不删除数据文件

16.hive的分桶分区原理

分区:
1、在Hive Select查询中一般会扫描整个表内容,会消耗很多时间做没必要的工作。有时候只需要扫描表中关心的一部分数据,因此建表时引入了partition概念。
2、分区表指的是在创建表时指定的partition的分区空间。
3、如果需要创建有分区的表,需要在create表的时候调用可选参数partitioned by。
静态分区应用场景:数据量不大,同时要知道分区的数据类型 (每个分区要写一个load data,缺点:load data效率低下,非常繁琐,不常用静态分区在业务中。)
动态分区应用场景:

不确定分区数量,数据量也不是很大,使用动态分区 实际工作中趋向于使用动态分区!!!
--注意:以下设置,只在当前的会话窗口有效
-- 1.打开动态分区模式:
set hive.exec.dynamic.partition=true;
-- 2.设置分区模式为非严格模式
set hive.exec.dynamic.partition.mode=nonstrict;

分桶:
数据分桶是将数据集分解为更容易管理的若干部分的另一种技术。将Hive 中数据划分成块,分区是粗粒度的划分,桶是细粒度的划分,这样做为了可以让查询发生在小范围提高效率。
Hive: 按照分桶字段(列)的hash值除以分桶的个数进行取余(bucket_id = column.hashcode % bucket.num)
分桶计算: hive 计算桶列的hash值再除以桶的个数取模,得到某条记录到底属于哪个桶。

参数设置: set hive.enforce.bucketing = true (临时设置,退出终端,再打开 就会恢复false)。

这个打开后,会自动根据bucket个数自动分配Reduce task个数,得到bucket的个数和reduce个数是一致的。

分桶应用场景:

1 数据抽样
在处理大规模数据集时,尤其载数据挖掘的阶段,可以用一份数据验证一下,代码是否可以运行成功,进行局部测试,也可以抽样进行一些代表性统计分析。
2 map-side join
可以获得更高的查询处理效率。连接两个在(包含连接列的)相同列上划分了桶的表,可以使用 Map 端连接(Map-side join)高效的实现。比如JOIN操作,对于JOIN操作两个表有一个相同的列,如果对这两个表都进行了桶操作。那么将保存相同列值的桶进行JOIN操作就可以提高效率。

17.hive中的 order by, sort by ,distribute by ,cluster by 区别?

order by 全局排序,最终会使用一个 reducetask 来完成排序,就算设置使用多个reducetask也没用,如果数据量很大使用order by来排序是不明智的。
sort by 局部排序,假设使用多个reducetask来运行的话,那么每个reducetask输出的结果都是有序的,但是所有的结果放在一起是没有顺序的

Sort By,它通常发生在每一个redcue里,“order by” 和“sort by"的区别在于,前者能给保证输出都是有顺序的,而后者如果有多个reduce的时候只是保证了输出的部分有序。set mapred.reduce.tasks=<number>在sort by可以指定,在用sort by的时候,如果没有指定列,它会随机的分配到不同的reduce里去。distribute by 按照指定的字段对数据进行划分到不同的输出reduce中 

distribute by
分桶查询,有两个条件
1.必须设置reducetask的个数:set mapreduce.job.reduces= (分桶个数)
2.查询中,必须设置 distribute by 来设置分桶规则:hash散列
注意:使用distribute by做hash散列排序时指定分区范围就可以达到范围分区的目的

cluster by
如果sort by 的字段和distribute by的字段一样,
就可以简写:cluster by age = distribute by age sort by age; 除此之外都不行

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值