基础回顾
-
H i v e 数据管理
- hive的表本质就是Hadoop的目录/文件
- hive默认表存放路径一般都是在你工作目录的hive目录里面,按表名做文件夹分开,如果你 有分区表的话,分区值是子文件夹,可以直接在其它的M/R job里直接应用这部分数据
-
Name HDFS Directory Table mobile_user /lbs/mobile_user Partition action = insight, day= 20190406 /lbs/mobile_user/action=insight/day=20190406 Bucket clusted by user into 32 buckets /lbs/mobile_user/action=insight/day=20190406/part-00000
-
H i v e 内部表和外部表
- Hive的create创建表的时候,选择的创建方式:
- – create table
- – create external table
- 特点:
- 在导入数据到外部表,数据并没有移动到自己的数据仓库目录下,也就是说外部表中的数据并不是由它 自己来管理的!而表则不一样;
- 在删除表的时候,Hive将会把属于表的元数据和数据全部删掉;而删除外部表的时候,Hive仅仅删除 外部表的元数据,数据是不会删除的!
- Hive的create创建表的时候,选择的创建方式:
-
H i v e 中的P a r t i t i o n
- 在 Hive 中,表中的一个 Partition 对应于表下的一个目录,所有的 Partition 的 数据都存储在对应的目录中
- – 例如:pvs 表中包含 ds 和 city 两个 Partition,则
- – 对应于 ds = 20190406, ctry = US 的 HDFS 子目录为:/wh/pvs/ds=20190406/ctry=US;
- – 对应于 ds = 20190406, ctry = CA 的 HDFS 子目录为;/wh/pvs/ds=20190406/ctry=CA
- partition是辅助查询,缩小查询范围,加快数据的检索速度和对数据按照一定的 规格和条件进行管理。
- 在 Hive 中,表中的一个 Partition 对应于表下的一个目录,所有的 Partition 的 数据都存储在对应的目录中
-
H i v e 中的B u c k e t
- hive中table可以拆分成partition,table和partition可以通过‘CLUSTERED BY ’进一步分bucket,bucket中的数据可以通过‘SORT BY’排序。
- create table bucket_user (id int,name string)clustered by (id) into 4 buckets;
- 'set hive.enforce.bucketing = true' 可以自动控制上一轮reduce的数量从而适配bucket的个数,当然,用户也可以自主设置mapred.reduce.tasks去适配 bucket个数
- Bucket主要作用:
- 数据sampling
- 提升某些查询操作效率,例如mapside join
- 查看sampling数据:
-
– hive> select * from student tablesample(bucket 1 out of 2 on id);
-
– tablesample是抽样语句,语法:TABLESAMPLE(BUCKET x OUT OF y)
-
– y必须是table总bucket数的倍数或者因子。hive根据y的大小,决定抽样的比例。例如,table总共分了64份,当y=32 时,抽取(64/32=)2个bucket的数据,当y=128时,抽取(64/128=)1/2个bucket的数据。x表示从哪个bucket开始抽 取。例如,table总bucket数为32,tablesample(bucket 3 out of 16),表示总共抽取(32/16=)2个bucket的数据 ,分别为第3个bucket和第(3+16=)19个bucket的数据。
-
H i v e 的优化
-
Map的优化:
- – 作业会通过input的目录产生一个或者多个map任务。set dfs.block.size(=128)
- – Map越多越好吗?是不是保证每个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;
-
-
如何适当的增加map数?
-
set mapred.map.tasks=10;
-
-
– Map端聚合 hive.map.aggr=true 。 Mr中的Combiners.
-
Reduce的优化:
- – hive.exec.reducers.bytes.per.reducer = 10;reduce任务处理的数据量
- – 调整reduce的个数:
- 设置reduce处理的数据量
- set mapred.reduce.tasks=100 (优先)
- 什么时候会存在只有1个reduce的情况?
- 1)没有group by
- 2)使用order by
- 优化:distribute by和sort by结合起来
- order by:全局排序
- sort by:不是全局排序,数据进入reducer之前做的排序
- distribute by:控制map端如何拆分数据给reduce
- 3)笛卡尔积
- 优化:join的时候用on
-
加快查询速度
-
分区裁剪(partition)
- – Where中的分区条件,会提前生效,不必特意做子查询,直接Join和GroupBy
-
笛卡尔积
- – join的时候不加on条件或者无效的on条件,Hive只能使用1个reducer来完成笛卡尔积
-
Map join
- – /*+ MAPJOIN(tablelist) */,必须是小表,不要超过1G,或者50万条记录
-
Union all
- – 先做union all再做join或group by等操作可以有效减少MR过程,尽管是多个Select,最终只有一个 mr
-
Multi-insert & multi-group by
- – 从一份基础表中按照不同的维度,一次组合出不同的数据
-
FROM from_statement INSERT OVERWRITE TABLE tablename1 [PARTITION (partcol1=val1)] select_statement1 group by key1 INSERT OVERWRITE TABLE tablename2 [PARTITION(partcol2=val2 )] select_statement2 group by key2
-
Automatic merge
- – 当文件大小比阈值小时,hive会启动一个mr进行合并
- – hive.merge.mapfiles = true 是否和并 Map 输出文件,默认为 True
- – hive.merge.mapredfiles = false 是否合并 Reduce 输出文件,默认为 False
- – hive.merge.size.per.task = 256*1000*1000 合并文件的大小
-
Multi-Count Distinct
- – 必须设置参数:set hive.groupby.skewindata=true;
- – select dt, count(distinct uniq_id), count(distinct ip) from ods_log where dt=20170301 group by dt ;
- 如果功能开启,目的是负载均衡
-
-
H i v e 的 J o i n 优化
-
注意:
- 一个MR job
- SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (a.key = c.key1)
- 生成多个MR job
- SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1)
- 一个MR job
-
表连接顺序
- 按照JOIN顺序中的最后一个表应该尽量是大表,因为JOIN前一阶段生成的数据会存在于 Reducer的buffer中,通过stream最后面的表,直接从Reducer的buffer中读取已经缓冲的中间 结果数据(这个中间结果数据可能是JOIN顺序中,前面表连接的结果的Key,数据量相对较小, 内存开销就小),这样,与后面的大表进行连接时,只需要从buffer中读取缓存的Key,与大表 中的指定Key进行连接,速度会更快,也可能避免内存缓冲区溢出。
- SELECT /*+ STREAMTABLE(a) */ a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1); a表被视为大表
- SELECT /*+ MAPJOIN(b) */ a.key, a.value FROM a JOIN b ON a.key = b.key;
MAPJION会把小表全部读入内存中,在map阶 段直接拿另外一个表的数据和内存中表数据做 匹配,由于在map是进行了join操作,省去了 reduce运行的效率也会高很多. - 左连接时,左表中出现的JOIN字段都保留,右表没有连接上的都为空。
- 1. SELECT a.val, b.val FROM a LEFT OUTER JOIN b ON (a.key=b.key) WHERE a.ds='2019-04-04' AND b.ds='2019-04-04'
- 2. SELECT a.val, b.val FROM a LEFT OUTER JOIN b ON (a.key=b.key AND b.ds='2019-04-04' AND a.ds='20019-04-04')
- 上面两种方式的结果是一致的,但是更好的是第二种!执行顺序是,首先完成2表JOIN,然后再通过WHERE条件进行过滤,这样在JOIN过程中可能会 输出大量结果,再对这些结果进行过滤,比较耗时。可以进行优化,将WHERE条件放在ON后 ,在JOIN的过程中,就对不满足条件的记录进行了预先过滤。
-
并行执行
- 同步执行hive的多个阶段,hive在执行过程,将一个查询转化成一个或者多个阶段。某个特 定的job可能包含众多的阶段,而这些阶段可能并非完全相互依赖的,也就是说可以并行执行 的,这样可能使得整个job的执行时间缩短。
- hive执行开启:set hive.exec.parallel=true
-
-
数据倾斜
- 操作
- Join
- Group by
- Count Distinct
- 原因
- key分布不均导致的
- 人为的建表疏忽
- 业务数据特点
- 症状
- 任务进度长时间维持在99%(或100%),查看任务监控页面,发现只有少量(1个或几个)reduce子任务未完成。
- 查看未完成的子任务,可以看到本地读写数据量积累非常大,通常超过10GB可以认定为发生数据倾斜。
- 倾斜度
- 平均记录数超过50w且最大记录数是超过平均记录数的4倍。
- 最长时长比平均时长超过4分钟,且最大时长超过平均时长的2倍。
- 万能方法
- hive.groupby.skewindata=true
- 操作
-
数据倾斜——大小表关联
-
原因
- Hive在进行join时,按照join的key进行分发,而在join左边的表的数据会首先读入内存,如果左边表的key相对 分散,读入内存的数据会比较小,join任务执行会比较快;而如果左边的表key比较集中,而这张表的数据量很大, 那么数据倾斜就会比较严重,而如果这张表是小表,则还是应该把这张表放在join左边。
-
思路
- 将key相对分散,并且数据量小的表放在join的左边,这样可以有效减少内存溢出错误发生的几率
- 使用map join让小的维度表先进内存。
-
方法
- Small_table join big_table
-
-
数据倾斜——大大表关联
-
原因
- 日志中有一部分的userid是空或者是0的情况,导致在用user_id进行hash分桶的时候,会将日志中userid为0或者 空的数据分到一起,导致了过大的斜率。
-
思路
- 把空值的key变成一个字符串加上随机数,把倾斜的数据分到不同的reduce上,由于null值关联不上,处理后并不 影响最终结果。
-
方法
- on case when (x.uid = '-' or x.uid = '0‘ or x.uid is null) then concat('dp_hive_search',rand()) else x.uid end = f.user_id;
-
-
数据倾斜——大大表关联(业务削减)
-
案例
- Select * from dw_log t join dw_user t1 on t.user_id=t1.user_id
- 现象:两个表都上千万,跑起来很悬
-
思路
- 当天登陆的用户其实很少
-
方法
-
Select/*+MAPJOIN(t12)*/ * from dw_log t11 join ( select/*+MAPJOIN(t)*/ t1.* from ( select user_id from dw_log group by user_id ) t join dw_user t1 on t.user_id=t1.user_id ) t12 on t11.user_id=t12.user_id;
-
-
-
数据倾斜——聚合时存在大量特殊值
-
原因
- 做count distinct时,该字段存在大量值为NULL或空的记录。
-
思路
- count distinct时,将值为空的情况单独处理,如果是计算count distinct,可以不用处理,直接过滤,在最后结 果中加1。
- 如果还有其他计算,需要进行group by,可以先将值为空的记录单独处理,再和其他计算结果进行union
-
方法
-
select cast(count(distinct(user_id))+1 as bigint) as user_cnt from tab_a where user_id is not null and user_id <> '';
-
-
-
数据倾斜——空间换时间
-
案例
- Select day,count(distinct session_id),count(distinct user_id) from log a group by day
-
问题
- 同一个reduce上进行distinct操作时压力很大
-
方法
-
select day, count(case when type='session' then 1 else null end) as session_cnt, count(case when type='user' then 1 else null end) as user_cnt from ( select day,session_id,type from ( select day,session_id,'session' as type from log union all select day user_id,'user' as type from log ) group by day,session_id,type ) t1 group by day
-
-

本文总结了Hive的基础知识,包括数据管理、内部表与外部表、分区和桶的概念。探讨了Hive的优化方法,如Map和Reduce的优化策略,如Map端聚合、调整Map和Reduce任务数量,以及通过分区裁剪、MapJoin和多插入优化查询性能。还提到了数据倾斜的问题及其解决办法,如数据分布均匀化和特定操作的优化策略。
8835

被折叠的 条评论
为什么被折叠?



