Hive优化

Hive的本质是MapReduce,优化其实大部分是对mapreduce的优化

hive优化目标:①横向增加并发,②纵向减少依赖

//开启mapjoin,默认为 true

• set hive.auto.convert.join = true;     

//开启map端数据聚合

• hive.map.aggr=true;

//负载均衡

• set hive.groupby.skewindata=true;

//开启任务并行执行

• set hive.exec.parallel=true; 

//小文件合并

• set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;     

//开启Map输出文件合并,默认为 True

• hive.merge.mapfiles = true;      
//开启Reduce输出文件合并,默认为 False

• hive.merge.mapredfiles = false; 
//开启本地mr

• set hive.exec.mode.local.auto=true;           

//开启hive中间数据传输压缩功能

• set hive.exec.compress.intermediate=true;        

//开启map端压缩功能

• set mapreduce.map.output.compress=true;       

//开启hive最终输出数据压缩功能

• set hive.exec.compress.output=true;          

//开启reduce端输出压缩功能

• set mapreduce.output.fileoutputformat.compress=true;         

//设置reduce个数

• set mapreduce.job.reduces = 5;           

//设置reduce个数(优先)

• set mapred.reduce.tasks=10;           

//设置map个数

• set mapred.map.tasks=10;          

//指明大表做流数据

• /*+ STREAMTABLE(a) */            

//指明小表

• /*+ MAPJOIN(b) */                 

//开启任务并行执行

•sethive.exec.parallel=true;   

1 Fetch抓取

Fetch抓取是指,Hive中对某些情况的查询可以不必使用MapReduce计算。例如:select * from employees;在这种情况下,Hive可以简单地读取employee对应的存储目录下的文件,然后输出查询结果到控制台。

         在hive-default.xml.template文件中hive.fetch.task.conversion默认是more,老版本hive默认是minimal,该属性修改为more以后,在全局查找、字段查找、limit查找等都不走mapreduce。

(1)  查看当前hive.fetch.task.conversion模式;

(2)  把hive.fetch.task.conversion设置成none,然后执行查询语句,都会执行mapreduce程序。

hive (default)> set hive.fetch.task.conversion=none;

hive (default)> select * from emp;

hive (default)> select ename from emp;

hive (default)> select ename from emp limit 3;

(3)  把hive.fetch.task.conversion设置成more,然后执行查询语句,如下查询方式都不会执行mapreduce程序。

hive (default)> set hive.fetch.task.conversion=more;

hive (default)> select * from emp;

hive (default)> select ename from emp;

hive (default)> select ename from emp limit 3;

2 本地模式

大多数的Hadoop Job是需要Hadoop提供完整的可扩展性来处理大数据集的。不过有时Hive的输入数据量是非常小的。在这种情况下,为查询触发执行任务时消耗可能会比实践job的执行时间要多的多。对于大多数这种情况,Hive可以通过本地模式在单台机器上处理所有的任务。对于小数据集,执行时间可以明显被缩减。

(1)  开启本地mr:set hive.exec.mode.local.auto=true;

(2)  设置local mr的最大输入数据量,当输入数据量小于这个值时采用local mr的方式,默认为134217728,即128M;

(3)  设置local mr的最大输入文件个数,当输入文件个数小于这个值时采用local mr的方式,默认为4;

案例实操:

(1)   开启本地模式,并执行查询语句

hive (default)>set hive.exec.mode.local.auto=true;

hive (default)>select * from emp cluster by deptno;

Time taken: 1.328 seconds, Fetched: 14 row(s)

(2) 关闭本地模式,并执行查询语句
hive (default)> sethive.exec.mode.local.auto=false;
hive (default)> select * from emp cluster by deptno;
Time taken: 20.09 seconds, Fetched: 14 row(s)

3 Map优化

Map个数主要的决定因素有:input的文件总数,input的文件大小,集群设置的文件块大小。

特殊情况:当文件为127m,字段只有一两个,却有几千万记录,应该考虑加map个数。

根据公式:computeSliteSize(Math.max(minSize,Math.min(maxSize,blocksize)))=blocksize=128M,调整 maxSize 最大值。让 maxSize 最大值低于 blocksize 就可以增加 map 的个数。

(1)  增加map个数

setmapred.map.tasks=10;(不一定生效)

setmapreduce.input.fileinputformat.split.maxsize=10000

(2)  减小map个数

setmapred.max.split.size=100000000;

setmapred.min.split.size.per.node=100000000;

setmapred.min.split.size.per.rack=100000000;

案例实操:

(1)执行查询

hive (default)>select count(*) from emp;

Hadoop job information for Stage-1: number of mappers: 1; numberof reducers: 1

(2)设置最大切片值为 100 个字节

hive (default)>set mapreduce.input.fileinputformat.split.maxsize=100;

hive (default)>select count(*) from emp;

Hadoop job information for Stage-1: number of mappers: 6; numberof reducers: 1

4 小文件合并

在map执行前合并小文件,减少map数:CombineHiveInputFormat具有对小文件进行合并的功能(系统默认的格式)。HiveInputFormat没有对小文件合并功能。

sethive.input.format= org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;

5 Reduce优化

reduce 个数并不是越多越好,过多的启动和初始化 reduce 也会消耗时间和资源;另外,有多少个 reduce,就会有多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题;

在设置 reduce 个数的时候也需要考虑这两个原则:处理大数据量利用合适的 reduce 数;使单个 reduce 任务处理数据量大小要合适。

(1)  Reduce个数计算公式

reduce个数 = InputFileSize(文件大小) / bytes per reducer(每个reduce能处理的文件大小)

(2)  每个Reduce处理的数据量默认是256MB

hive.exec.reducers.bytes.per.reducer=256000000

(3)  每个任务最大的reduce数,默认是1009

hive.exec.reducers.max=1009

(4)   调整reduce的个数:

setmapred.reduce.tasks=10(优先)

(5)   reduce任务处理的数据量

set hive.exec.reducers.bytes.per.reducer=10

(6)  修改hadoop配置文件mapred-default.xml,设置每个job的Reduce个数

setmapreduce.job.reduces = 15;

一个reduce会消耗很长时间,建议加reduce,什么时候会存在一个Reduce的情况:

(a)   没有group by(对某一字段进行聚合)

(b)   使用order by(优化:可以使用distribute by和sort by)

       order by:全局排序

       sort by:不是全局排序,数据进入reduce之前做的排序

       distribute by:控制map端如何拆分数据给reduce

(c)    笛卡尔积:两两取对的时候会出现笛卡尔积,也就join时候

badcase:(不好的案例)

select userid, xxx from A_table A

join B_table B

where A.userid == B.userid

goodcase:(好的案例)

select userid, xxx from A_table A

join B_table B

on A.userid == B.userid

做join就相当于笛卡尔积的过程,用where不用on,也是以一个reduce来运行的,耗时会更长,两个表做join,条件是放到on里面

6 压缩

压缩有三部分:第一是输入数据压缩;第二是map输出压缩;第三是reduce输出压缩。

6.1 开启Map输出压缩

开启map输出阶段压缩可以减少job中map和reduce task间数据传输量。

案例实操:
(1) 开启 hive 中间传输数据压缩功能
hive (default)>set hive.exec.compress.intermediate=true;

(2) 开启 mapreduce 中 map 输出压缩功能
hive (default)>set mapreduce.map.output.compress=true;

(3) 设置 mapreduce 中 map 输出数据的压缩方式

hive (default)>setmapreduce.map.output.compress.codec= org.apache.hadoop.io.compress.SnappyCodec;

(4) 执行查询语句
hive (default)> select count(ename) name from emp;

6.2 开启reduce输出阶段压缩

当hive将输出写入到表中时,输出内容同样可以进行压缩。属性hive.exec.compress.output控制着这个功能。用户可能需要保持默认设置文件中的默认值false,这样默认的输出就是非压缩的纯文本文件了。用户可以通过在查询语句或执行脚本中设置这个值为true,来开启输出压缩功能。

案例实操:
(1) 开启 hive 最终输出数据压缩功能

hive (default)>set hive.exec.compress.output=true;

(2) 开启 mapreduce 最终输出数据压缩
hive (default)>setmapreduce.output.fileoutputformat.compress=true;

(3) 设置 mapreduce 最终数据输出压缩方式
hive (default)> set mapreduce.output.fileoutputformat.compress.codec=
org.apache.hadoop.io.compress.SnappyCodec;

(4) 设置 mapreduce 最终数据输出压缩为块压缩
hive (default)> setmapreduce.output.fileoutputformat.compress.type=BLOCK;

(5) 测试一下输出结果是否是压缩文件
hive (default)> insert overwrite local directory'/opt/module/datas/distribute-result' select * from emp distribute by deptno sort by empno desc;

7 小表Join大表

Hive在进行join时,按照join的key进行分发,而在join左边的表的数据会首先读入内存,如果左边表的key相对分散,读入内存的数据会比较小,join任务执行会比较快;而如果左边的表key比较集中,而这张表的数据量很大,那么数据倾斜就会比较严重(这边应该指明小表,下面会讲),而如果这张表是小表,则还是应该把这张表放在join左边。

将key相对分散,并且数据量小的表放在join的左边,这样可以有效减少内存溢出错误发生的几率;再进一步,可以使用Group让小的维度表(1000条以下的记录条数)先缓存进内存,在map端完成reduce。

         实际测试发现:新版的hive已经对小表JOIN大表和大表JOIN小表进行了优化。小表放在左边和右边已经没有明显区别。

方法:small_table join big_table

案例实操

需求:测试大表 JOIN 小表和小表 JOIN 大表的效率

(1)建大表、小表和JOIN 后表的语句

create tablebigtable(id bigint, time bigint, uid string, keyword string, url_rank int,click_num int, click_url string) row format delimited fields terminated by'\t';

create tablesmalltable(id bigint, time bigint, uid string, keyword string, url_rank int,click_num int, click_url string) row format delimited fields terminated by'\t';

create tablejointable(id bigint, time bigint, uid string, keyword string, url_rank int,click_num int, click_url string) row format delimited fields terminated by'\t';

(2)分别向大表和小表中导入数据

hive(default)> load data local inpath '/opt/module/datas/bigtable' into tablebigtable;
hive (default)>load data local inpath '/opt/module/datas/smalltable' intotable smalltable;

(3)关闭 mapjoin功能(默认是打开的)
set hive.auto.convert.join = false;

(4)执行小表 JOIN 大表语句

insert overwritetable jointable
select b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url
from smalltable s
left join bigtable b

on b.id = s.id;

(5)执行大表 JOIN 小表语句

insert overwritetable jointable
select b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url
from bigtable b
left join smalltable s
on s.id = b.id;

8 大表join大表

日志中有一部分的key是空或者是0的情况,导致在用key进行hash分桶的时候,会将日志中key为0或者空的数据分到一起,导致了过大的斜率。

8.1 过滤空key

案例实操

(1)  创建原始数据表、空id表、合并后数据表

create tableori(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';


create table nullidtable(id bigint, time bigint, uid string, keyword string,url_rank int, click_num int, click_url string) row format delimited fields terminated by'\t';


create table jointable(id bigint, time bigint, uid string, keyword string,url_rank int, click_num int, click_url string) row format delimited fields terminated by'\t';

(2)  分别加载原始数据和空id 数据到对应表中

hive (default)> load data local inpath'/opt/module/datas/ori' into table ori;
hive (default)> load data local inpath '/opt/module/datas/nullid' into tablenullidtable;

(4)  测试不过滤空id(不过滤空id,join)

hive (default)> insert overwrite table jointable
select n.* from nullidtable n left join ori o on n.id = o.id;
Time taken: 42.038 seconds

(5)  测试过滤空id(过滤掉空id再join)

hive (default)> insert overwrite table jointable
select n.* from (select * from nullidtable where id is not null ) n left joinori o on n.id =
o.id;
Time taken: 31.725 seconds

8.2 赋值空key

有时虽然某个key为空对应的数据很多,但是相应的数据不是异常数据,必须要包含在join的结果中,此时我们可以表a中的key为空的字段赋予一个随机的值,使得数据随机均匀地分布到不同的reduce上,把倾斜的数据分到不同的reduce上,由于null值关联不上,处理后并不影响最终结果。

案例实操:

不随机分布空null 值:

(1)    设置 5 个 reduce 个数

set mapreduce.job.reduces = 5;

(2)    JOIN 两张表

先清空表数据:truncate tablejointable;

再join:

insert overwritetable jointable
select n.* from nullidtable n left join ori b on n.id = b.id;

(3)    再到历史服务器中查看reduce运行任务的时间

结果:可以看出来,出现了数据倾斜,某些reducer 的资源消耗远大于其他 reducer

随机分布空null值

(1)  设置 5 个 reduce 个数

set mapreduce.job.reduces = 5;

(2)  JOIN 两张表

先清空表数据:truncate tablejointable;

再join:

insert overwritetable jointable
select n.* from nullidtable n full join ori o on case when n.id is null thenconcat('hive', rand()) else n.id end = o.id;

(3)  再到历史服务器中查看reduce运行任务的时间

结果:可以看出来,消除了数据倾斜,负载均衡reducer 的资源消耗

9 mapjoin

mapjoin类似一种检索配置文件的形式,如果不知道mapjoin或者不符合mapjoin的条件,那么hive解析器会将join操作转换成commonjoin,即:在reduce端阶段完成join。容易发生数据倾斜。可以用mapjoin把小表加载到内存在map端进行join,避免reduce处理。

mapjoin工作机制:

mapjion参数设置:                     

(1)  设置自动选择mapjoin

sethive.auto.convert.join = true; 默认为 true

(2)  大表小表的阈值设置(默认25m以下是小表)

sethive.mapjoin.smalltable.filesize=25000000;

(3)  也可以直接指定小表:/*+ MAPJOIN(small_table) */

案例实操:

(1)  开启mapjoin功能

set hive.auto.convert.join = true; 默认为 true

(2)  执行小表join大表语句

先清空表数据:truncate tablejointable;

insert overwritetable jointable

select b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url
from smalltable s
join bigtable b
on s.id = b.id;

(3)  执行大表join小表语句

先清空表数据:truncate tablejointable;

insert overwritetable jointable
select b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url
from bigtable b
join smalltable s
on s.id = b.id;

跟前面关闭mapjoin执行join的运行时间对比!

10 group by

默认情况下,map阶段同key数据分发给一个reduce,当一个key数据过大时就会发生数据倾斜。并不是所有的聚合操作都需要在reduce端完成,很多聚合操作都可以先在map端进行部分聚合,最后在reduce端得出结果。

开启map端聚合参数设置

(1)  是否在map端进行聚合,默认为ture

hive.map.aggr = true

(2)  在map端进行聚合操作的条目数目

hive.groupby.mapaggr.checkinterval = 100000

(3)  有数据倾斜的时候进行负载均衡

hive.groupby.skewindata = true

当选项设定为ture,生成的查询计划会有两个mrjob。第一个mr job中,map的输出结果会随机分布到reduce中,每个reduce做部分聚合操作,并输出结果,这样处理的结果是相同的group by key有可能被分发到不同的reduce中,从而达到负载均衡的目的;第二个mr job再根据预处理的数据结果按照group by key分布到reduce中(这个过程可以保证相同的group by key被分布到同一个reduce中),最后完成最终的聚合操作。

(两次聚合:第一次:mr1聚合分发出r1、r2;第二次:mr2聚合r1、r2得到新的reduce,新的reduce,key是相同的,二级预处理)

 

11conut(Distinct)去重统计

数据量小的时候无所谓,数据量大的时候,由于count distinct操作需要用一个reduce task来完成,这一个reduce需要处理的数据量太大,就会导致整个job很难完成,一般conunt distinct使用先group by再count的方式替换。

(1)创建一张大表

hive (default)> create table bigtable(id bigint, time bigint,uid string, keyword string,
url_rank int, click_num int, click_url string) row format delimited fieldsterminated by
'\t';
(2)加载数据
hive (default)> load data local inpath'/opt/module/datas/bigtable' into table bigtable;
(3)设置 5 个 reduce 个数
set mapreduce.job.reduces = 5;
(4)执行去重 id 查询hive (default)> select count(distinct id) from bigtable;
Stage-Stage-1: Map: 1 Reduce: 1 Cumulative CPU:7.12 sec HDFS Read:
120741990 HDFS Write: 7 SUCCESS


(5)采用 GROUP by 去重 id
hive (default)> select count(id) from (select id frombigtable group by id) a;
Stage-Stage-1: Map: 1 Reduce: 5 Cumulative CPU:17.53 sec HDFS Read:
120752703 HDFS Write: 580 SUCCESS
Stage-Stage-2: Map: 3 Reduce: 1 Cumulative CPU: 4.29 sec HDFS Read: 9409 HDFS Write: 7 SUCCESS

虽然会多用一个 Job 来完成,但在数据量大的情况下,这个绝对是值得的。

12 Union all与Union

表T1:A B C

表T2:B C D

Union:A B C D

Union all:A B C B C D

Union去重合并操作, Union all是不去重合并操作,union all直接相加处理,操作简单,先做union all再做join或group by等操作可以有效减少MR过程,尽管是多个Select,最终只有一个mr。

13 Multi-insert & multi-group by

从一份基础表中按照不同的维度,一次组合出不同的数据,就是多个任务减少为一个任务同时执行完,多个mr减少为一个mr。

FROM from_statement(对应下面两个语句,同时执行的)

INSERTOVERWRITE TABLE tablename1 [PARTITION (partcol1=val1)] select_statement1 groupby key1

INSERTOVERWRITE TABLE tablename2 [PARTITION(partcol2=val2 )] select_statement2 groupby key2

A分别写入数据给BC,优化后,A同时给BC写数据

14 Automatic merge(自动合并)

 文件数目过多,会给 HDFS 带来压力,并且会影响处理效率,可以通过合并 Map 和 Reduce 的结果文件来尽量消除这样的影响

当文件大小比阈值小时,hive会启动一个mr进行合并

hive.merge.mapfiles= true 是否合并Map输出文件,默认为 True

hive.merge.mapredfiles= false 是否合并Reduce输出文件,默认为 False

hive.merge.size.per.task= 256*1000*1000 合并文件的大小

15 join优化--MR个数

按照如下方式,产生一个MR:

SELECT a.val, b.val, c.val

FROM a

JOIN b ON (a.key = b.key1)

JOIN c ON (a.key = c.key1)

按照如下方式,产生多个MR:

SELECT a.val, b.val, c.val

FROM a

JOIN b ON (a.key = b.key1)

JOINc ON (c.key = b.key1) 

 

16 Join优化--表连接顺

按照JOIN顺序中的最后一个表应该尽量是大表,因为JOIN前一阶段生成的数据会存在于Reducer的buffer中,通过stream最后面的表,直接从Reducer的buffer中读取已经缓冲的中间结果数据(这个中间结果数据可能是JOIN顺序中,前面表连接的结果的Key,数据量相对较小,内存开销就小),这样,与后面的大表进行连接时,只需要从buffer中读取缓存的Key,与大表中的指定Key进行连接,速度会更快,也可能避免内存缓冲区溢出。

虽然这里是a join bc,但前面使用了/*+ STREAMTABLE(a) */,指明a是大表,做流数据并行执行,bc被视为小表进行缓存

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);

MAPJION会把小表全部读入内存中,在map阶段直接拿另外一个表的数据和内存中表数据做匹配,由于在map是进行了join操作,省去了reduce运行的效率也会高很多

SELECT /*+ MAPJOIN(b) */ a.key, a.value

FROM a

JOIN b ON a.key = b.key;

A.join(B)按照这种顺序join的话,默认B是大表,reduce会尝试把B前面的表进行缓存,把A表放到内存区,对B进行遍历,进行join,B表的记录是n条,那遍历B表的时间复杂度就是O(n),所以小表join大表,这样对时间复杂度有很好的优化

A m, B n

O(m*n)

Hive会默认把左边的数据放到内存,右边的数据做类似流数据(stream)

指明大表做流数据:/*+ STREAMTABLE(a) */

指明小表:/*+ MAPJOIN(b) */

最好每次写join的时候,小表放左边,大表放右边

17 join优化--where/on

左连接时,左表中出现的JOIN字段都保留,右表没有连接上的都为空

SELECT a.val, b.val

FROM a

LEFT OUTER JOIN b ON (a.key=b.key)

WHERE a.ds=’2008-08-22’ AND b.ds=’2008-08-22’

ASELECT a.val, b,val

FROM a

LEFT OUTER JOIN b

ON (a.key=b.key AND b.ds=’ 2008-08-22’ AND a.ds=’2008-08-22’)

第一个表是先完成2表JOIN,然后再通过where条件进行过滤,这样在join过程中可能会输出大量结果,再对这些结果进行过滤,比较耗时。可以进行优化,第二个表将where条件放在on后,在join的过程中,就对不满足条件的记录进行了预先过滤。

18 并行执行

   Hive 会将一个查询转化成一个或者多个阶段。这样的阶段可以是 MapReduce 阶段、抽样阶段、合并阶段、 limit 阶段,或者Hive执行过程中可能需要的其他阶段。默认情况下,Hive 一次只会执行一个阶段。不过,某个特定的job可能包含众多的阶段,而这些阶段可能并非完全互相依赖的,也就是说有些阶段是可以并行执行的,这样可能使得整个job的执行时间缩短。不过如果有更多的阶段可以并行执行,那么可能就越快完成。

一个执行过程的遍历,些过程是没必要串行的,如下面1和3是可以并行的,开启并行设置即会自动查看哪些是可以合并执行的。

通过设置参数hive.exec.parallel值为true,就可以开启并发执行。不过,在共享集群中,需要注意下,如果job中并行阶段增多,那么集群利用率就会增加。

set hive.exec.parallel=true;  //开启任务并行执行

set hive.exec.parallel.thread.number=16;  //同一个sql允许最大并行度,默认为8。

19 笛卡尔积

尽量避免笛卡尔积,join的时候不加on条件,或者无效的on条件,hive只能使用1个reduce来完成笛卡尔积。

20 行列过滤

(京东面试)

列处理:在select中只拿需要的列,尽量使用分区过滤,少用select *。

行处理:在分区剪裁中,当使用外关联时,如果将副表的过滤条件写在where后面,那么就会先全表关联,之后再过滤

案例实操:

(1)测试先关联两张表,再用where条件过滤

hive (default)>select o.id from bigtable b

join ori o on o.id =b.id

where o.id <= 10;

Time taken: 34.406seconds, Fetched: 100 row(s)

(2)通过子查询后,再关联表

hive (default)>select b.id from bigtable b

join (select id fromori where id <= 10 ) o on b.id = o.id;

Time taken: 30.058seconds, Fetched: 100 row(s)

21 动态分区调整

 Hive中的每个分区都对应hdfs上的一个目录,分区列也不是表中的一个实际的字段,而是一个或者多个伪列,在表的数据文件中实际上并不保存分区列的信息与数据。Partition关键字中排在前面的为主分区(只有一个),后面的为副分区,在默认情况下,hive会假设主分区使用静态分区,副分区使用动态分区。

静态分区:静态分区在加载数据和使用时都需要在sql语句中指定;

   例:(stat_date=’20120625’,province=’hunan’)

关系型数据库中,对分区表insert数据时候,数据库自动会根据分区字段的值,将数据插入到相应的分区中,hive中也提供了类似的机制,即动态分区(aynamic partition),只不过,使用hive的动态分区,需要进行相应的配置。

动态分区参数设置

(1)  开启动态分区功能(默认开启)

hive.exec.dynamic.partition=true

(2)  设置为非严格模式(动态分区的模式,默认strict,表示必须指定至少一个分区为静态分区,nonstrict模式表示允许所有的分区字段都可以使用动态分区。)

hive.exec.dynamic.partition.mode=nonstrict

(3)  在所有执行mr的节点,最大一共可以创建多少个动态分区

hive.exec.max.dynamic.partitions=1000

(4)  在每个执行mr的节点上,最大可以创建多少个动态分区。该参数需要根据实际的数据来设定。比如:源数据中包含了一年的数据,即day字段有365个值,那么该参数就需要设置成大于365,如果使用默认值100,则会报错。

hive.exec.max.dynamic.partitions.pernode=100

(5)  整个mr job中,最大可以创建多少个hdfs文件

hive.exec.max.created.files=100000

(6)  当有空分区生成时,是否抛出异常。一般不需要设置

hive.error.on.empty.partition=false

案例操作

需求:将 ori中的数据按照时间(如:20111230000008),插入到目标表 ori_partitioned_target的相应分区中。

(1)  创建分区表

create tableori_partitioned(id bigint, time bigint, uid string, keyword string, url_rankint,click_num int, click_url string)
partitioned by (p_time bigint)
row format delimited fields terminated by '\t';

(2)  加载数据到分区表中

hive (default)>load data local inpath '/opt/module/datas/ds1' into table ori_partitioned partition(p_time='20111230000010');
hive (default)> load data local inpath '/opt/module/datas/ds2' into tableori_partitioned partition(p_time='20111230000011') ;

(3)  创建目标分区表

create tableori_partitioned_target(id bigint, time bigint, uid string, keyword string,
url_rank int, click_num int, click_url string) PARTITIONED BY (p_time STRING)row format delimited fields terminated by '\t';

(4)  设置动态分区

sethive.exec.dynamic.partition = true;
set hive.exec.dynamic.partition.mode = nonstrict;
set hive.exec.max.dynamic.partitions = 1000;
set hive.exec.max.dynamic.partitions.pernode = 100;
set hive.exec.max.created.files = 100000;
set hive.error.on.empty.partition = false;
hive (default)> insert overwrite table ori_partitioned_target partition(p_time)
select id, time, uid, keyword, url_rank, click_num, click_url, p_time fromori_partitioned;

(5)  查看目标分区

hive (default)>show partitions ori_partitioned_target;

22 严格模式

Hive 提供了一个严格模式,可以防止用户执行那些可能意想不到的不好的影响的查询。通过设置属性 hive.mapred.mode 值为默认是非严格模式 nonstrict 。开启严格模式需要修改 hive.mapred.mode 值为 strict,开启严格模式可以禁止 3 种类型的查询。

<property>
<name>hive.mapred.mode</name>
<value>strict</value>
</property>

(1) 对于分区表,除非where语句中含有分区字段过滤条件来限制范围,否则不允许执行。换句话说,就是用户不允许扫描所有分区。进行这个限制的原因是,通常分区表都拥有非常大的数据集,而且数据增加迅速。没有进行分区限制的查询可能会消耗令人不可接受的巨大资源来处理这个表。

(2) 对于使用了order by语句的查询,要求必须使用limit语句。因为order by为了执行排序过程会将所有的结果数据分发到同一个Reducer中进行处理,强制要求用户增加这个LIMIT语句可以防止Reducer额外执行很长一段时间。

(3) 限制笛卡尔积的查询。对关系型数据库非常了解的用户可能期望在执行 JOIN 查询的时候不使用 ON 语句而是使用 where 语句,这样关系数据库的执行优化器就可以高效地将WHERE 语句转化成那个 ON 语句。不幸的是, Hive 并不会执行这种优化,因此,如果表足够大,那么这个查询就会出现不可控的情况。

23 JVM重用

JVM 重用是Hadoop调优参数的内容,其对hive的性能具有非常大的影响,特别是对于很难避免小文件的场景或task特别多的场景,这类场景大多数执行时间都很短。Hadoop的默认配置通常是使用派生JVM 来执行map和Reduce任务的。这时JVM的启动过程可能会造成相当大的开销,尤其是执行的job包含有成百上千task任务的情况。JVM重用可以使得 JVM实例在同一个job中重新使用N次。N的值可以在Hadoop的mapred-site.xml文件中进行配置。通常在10-20之间,具体多少需要根据具体业务场景测试得出。

<property>
<name>mapreduce.job.jvm.numtasks</name>
<value>10</value>
</property>

这个功能的缺点是,开启 JVM 重用将一直占用使用到的 task 插槽,以便进行重用,直到任务完成后才能释放。如果某个“不平衡的”job 中有某几个reduce task执行的时间要比其他Reduce task消耗的时间多的多的话,那么保留的插槽就会一直空闲着却无法被其他的job使用,直到所有的task都结束了才会释放。

24 推测执行

在分布式集群环境下,因为程序 Bug(包括 Hadoop 本身的 bug),负载不均衡或者资源分布不均等原因,会造成同一个作业的多个任务之间运行速度不一致,有些任务的运行速度可能明显慢于其他任务(比如一个作业的某个任务进度只有50%,而其他所有任务已经运行完毕),则这些任务会拖慢作业的整体执行进度。为了避免这种情况发生, Hadoop 采用了推测执行(Speculative Execution)机制,它根据一定的法则推测出“拖后腿”的任务,并为这样的任务启动一个备份任务,让该任务与原始任务同时处理同一份数据,并最终选用最先成功运行完成任务的计算结果作为最终结果。
设置开启推测执行参数: Hadoop的mapred-site.xml文件中进行配置

<property>
<name>mapreduce.map.speculative</name>
<value>true</value>
<description>If true, then multiple instances of some map tasks
may be executed in parallel.</description>
</property>
<property>
<name>mapreduce.reduce.speculative</name>
<value>true</value>
<description>If true, then multiple instances of some reduce tasks
may be executed in parallel.</description>
</property>

不过hive本身也提供了配置项来控制reduce-side的推测执行:

<property>
<name>hive.mapred.reduce.tasks.speculative.execution</name>
<value>true</value>
<description>Whether speculative execution for reducers should be turnedon.
</description>
</property>

关于调优这些推测执行变量,还很难给一个具体的建议。如果用户对于运行时的偏差非常敏感的话,那么可以将这些功能关闭掉。如果用户因为输入数据量很大而需要执行长时间的map或者Reduce task的话,那么启动推测执行造成的浪费是非常巨大大。

25 执行计划(explain)

基本语法
EXPLAIN [EXTENDED | DEPENDENCY | AUTHORIZATION] query
案例实操
(1) 查看下面这条语句的执行计划
hive (default)> explain select * from emp;
hive (default)> explain select deptno, avg(sal) avg_sal from emp group bydeptno;
(2) 查看详细执行计划
hive (default)> explain extended select * from emp;
hive (default)> explain extended select deptno, avg(sal) avg_sal from empgroup by deptno;

26 数据倾斜

概念:数据倾斜是指,map /reduce程序执行时,reduce节点大部分执行完毕,但是有一个或者几个reduce节点运行很慢,导致整个程序的处理时间很长,这是因为某一个key的条数比其他key多很多(有时是百倍或者千倍之多),这条key所在的reduce节点所处理的数据量比其他节点就大很多,从而导致某几个节点迟迟运行不完。

产生数据倾斜操作:join、group by、count distinct

1.其中一个表较小,但是key集中,可能会导致分发到一个或几个Reduce上的数据远高于平均值;

 2.大表join大表,空值或者0比较多,这些控制都由一个reduce处理,非常慢;

 3.group by维度过小,某值的数量过多。处理某值的Reduce非常耗时;

 4.count distinct某特殊值过多,处理此特殊值的Reduce耗时。

原因:

1.key分布不均导致的;

2.业务数据本身的特性;

3.建表时考虑不周;

4.某些语句本身就有数据倾斜

症状:

1.任务进度长时间维持在99%(或100%),查看任务监控页面,发现只有少量(1个或几个)reduce子任务未完成。

2.查看未完成的子任务,可以看到本地读写数据量积累非常大,通常超过10GB可以认定为发生数据倾斜。

倾斜度:

1.平均记录数超过50w且最大记录数是超过平均记录数的4倍。

2.最长时长比平均时长超过4分钟,且最大时长超过平均时长的2倍。

万能方法:

• hive.groupby.skewindata=true  //开启数据聚合

27 数据倾斜案例

大大表关联(业务消减)

•案例

Select * from dw_log t join dw_user t1 ont.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 asbigint) as user_cnt

from tab_a

where user_id is not null and user_id<> ''

空间换时间

•案例

Select day,count(distinctsession_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

空值产生的数据倾斜

场景:

如日志中,常会有信息丢失的问题,比如日志中的 user_id,如果取其中的 user_id 和用户表中的user_id 关联,会碰到数据倾斜的问题。

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

select * from log a

 join users b

  ona.user_id is not null

  anda.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 users b

on case when a.user_id is null thenconcat(‘hive’,rand() ) else a.user_id end = b.user_id;

结论:方法2比方法1效率更好,不但io少了,而且作业数也少了。解决方法1中 log读取两次,jobs是2。解决方法2 job数是1 。这个优化适合无效 id (比如 -99 , ’’, null 等) 产生的倾斜问题。把空值的 key 变成一个字符串加上随机数,就能把倾斜的数据分到不同的reduce上 ,解决数据倾斜问题。

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

场景:用户表中user_id字段为int,log表中user_id字段既有string类型也有int类型。当按照user_id进行两个表的Join操作时,默认的Hash操作会按int型的id来进行分配,这样会导致所有string类型id的记录都分配到一个Reducer中。

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

select * from users a

 left outer join logs b

  ona.usr_id = cast(b.user_id as string)

小表不小不大,怎么用map join 解决倾斜问题

使用map join解决小表(记录数少)关联大表的数据倾斜问题,这个方法使用的频率非常高,但如果小表很大,大到map join会出现bug或异常,这时就需要特别的处理。以下例子:

select * from log a

 left outer join users b

  ona.user_id = b.user_id;

users 表有 600w+ 的记录,把 users 分发到所有的 map 上也是个不小的开销,而且map join 不支持这么大的小表。如果用普通的 join,又会碰到数据倾斜的问题。

假如,log里user_id有上百万个,这就又回到原来map join问题。所幸,每日的会员uv不会太多,有交易的会员不会太多,有点击的会员不会太多,有佣金的会员不会太多等等。所以下面这个方法能解决很多场景下的数据倾斜问题。

解决方法:

select /*+mapjoin(x)*/* from log a

 left outer join (

   select  /*+mapjoin(c)*/d.*

     from ( select distinct user_id from log ) c

     join users d

     on c.user_id = d.user_id

    ) x

  ona.user_id = b.user_id;

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值