第4节 hive调优:1、2、fetch抓取和表的优化

hive的调优:
第一个调优:fetch抓取,能够避免使用mr的,就尽量不要用mr,因为mr太慢了
set hive.fetch.task.conversion=more 表示我们的全局查找,字段查找,limit查找都不走mr
这个属性配置有三个取值 more minimal none 如果配置成none,所有的都要走mr程序

hive的本地模式:
set hive.exec.mode.local.auto=true 开启本地模式,解决多个小文件输入的时候,分配资源时间超过数据的计算时间
set hive.exec.mode.local.auto.inputbytes.max=51234560; 设置输入的数据临界值,如果小于这值都认为是小任务模式,启动本地模式来执行
set hive.exec.mode.local.auto.input.files.max=10; 设置输入文件个数的临界值,如果小于这个数量,那么也认为是小任务模式

注:mapreduce中还有一个小任务模式:

hadoop当中的小任务模式 ubermode:
启动一个maptask,分配资源花了30s,然后maptask去处理一个小文件,花了3s
hadoop当中的小任务模式:
mapreduce.job.ubertask.enable 设置我们需要开启hadoop任务的小任务模式 小任务模式可以根据我们输入的数据量做判断,如果输入的数据量比较小
输入10个文件,每个文件2KB,输入的数据总量也就是20KB,10个小文件,占用10个block块,每个block块对应要启动一个maptask
可以考虑使用ubermode 小任务模式来实现所有的数据就在一个maptask里面去处理。

set mapreduce.job.ubertask.enable; //查看该参数的值。

set mapreduce.job.ubertask.enable=true; //设置该参数的值



第二个优化:hive表的优化
去重的优化:
select count(distinct s_id) from score;这种写法所有的去重数据都会在一个reduce当中去,造成数据处理比较慢
select count(1) from (select s_id from score group by s_id) bysid; 这种写法,使用了一个嵌套子查询,先对数据进行group by去重,然后再进行统计
尽量避免大sql,可以将一个很大的sql拆成多段,分步的去执行

大表join大表的优化:
空key的过滤

不过滤:
INSERT OVERWRITE TABLE jointable
SELECT a.* FROM nullidtable a JOIN ori b ON a.id = b.id;
结果:
No rows affected (152.135 seconds)

过滤:过滤掉我们所有的为null的id,使得我们的输入数据量变少
INSERT OVERWRITE TABLE jointable
SELECT a.* FROM (SELECT * FROM nullidtable WHERE id IS NOT NULL ) a JOIN ori b ON a.id = b.id;
结果:
No rows affected (141.585 seconds)

空key的转换
如果规定这些空key过滤不调,那么我们可以对空key进行转换
SELECT a.*
FROM nullidtable a
LEFT JOIN ori b ON CASE WHEN a.id IS NULL THEN 'hive' ELSE a.id END = b.id;
如果空key比较多,那么就会将大量的空key转换成 hive,那么就会遇到一个问题,数据倾斜
数据倾斜的表现形式:有一个reduce处理的数据量远远比其他reduce处理的数据量要大,造成其他的reduce数据都处理完了,这个还没处理完

怎么发现的数据倾斜,如何出现的数据倾斜,怎么解决的数据倾斜

空key的打散
SELECT a.*
FROM nullidtable a
LEFT JOIN ori b ON CASE WHEN a.id IS NULL THEN concat('hive', rand()) ELSE a.id END = b.id;
通过将空key打散成不同的随机字符串,就可以解决我们hive的数据倾斜的问题

hive第三个优化:map端的join
hive已经开启了自动的map端的join功能,不管是我们的大表join小表,还是小表join大表,都会将我们的小表加载到内存当中来
首先第一步:启动一个local的task,寻找哪个表的数据是小表数据


hive的group by优化:能在map端聚合的数据,就尽量在map端进行聚合
多加一层mr的程序,让我们的数据实现均衡的负载,避免数据的倾斜

count(distinct)的优化:
这种写法效率低下:SELECT count(DISTINCT id) FROM bigtable;
可以准换成这种写法:SELECT count(id) FROM (SELECT id FROM bigtable GROUP BY id) a;

笛卡尔积:任何时候都要避免笛卡尔积,避免无效的on条件
select from A left join B -- on A.id = B.id

使用分区裁剪,列裁剪:
分区裁剪:如果是我们的分区表,那么查询的时候,尽量带上我们的分区条件
列裁剪:尽量避免使用select * ,需要查询哪些列,就选择哪些列

动态分区调整:
分区表的数据加载两种方式:
load data inpath '/export/xxx' into table xxx partition(month = 'xxx')
insert overwrite table xxx partition (month = 'xxx') select xxx

使用动态分区动态的添加数据
如果要使用动态分区添加数据,最后一个字段一定要是我们的分区字段
INSERT overwrite TABLE ori_partitioned_target PARTITION (p_time)
SELECT id, time, uid, keyword, url_rank, click_num, click_url, p_time
FROM ori_partitioned;

==========================================================

九、调优

9.1 Fetch抓取(Hive可以避免进行MapReduce)

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

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

<property>

    <name>hive.fetch.task.conversion</name>

    <value>more</value>

    <description>

      Expects one of [none, minimal, more].

      Some select queries can be converted to single FETCH task minimizing latency.

      Currently the query should be single sourced not having any subquery and should not have

      any aggregations or distincts (which incurs RS), lateral views and joins.

      0. none : disable hive.fetch.task.conversion

      1. minimal : SELECT STAR, FILTER on partition columns, LIMIT only 所有的都要走MR

      2. more    : SELECT, FILTER, LIMIT only (support TABLESAMPLE and virtual columns)

    </description>

  </property>

案例实操:

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

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

hive (default)> select * from score;

hive (default)> select s_score from score;

hive (default)> select s_score from score limit 3;

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

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

hive (default)> select * from score;

hive (default)> select s_score from score;

hive (default)> select s_score from score limit 3;

9.2 本地模式

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

用户可以通过设置hive.exec.mode.local.auto的值为true,来让Hive在适当的时候自动启动这个优化。

set hive.exec.mode.local.auto=true;  //开启本地mr

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

set hive.exec.mode.local.auto.inputbytes.max=51234560;

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

set hive.exec.mode.local.auto.input.files.max=10;

案例实操:

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

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

hive (default)> select * from score cluster by s_id;

18 rows selected (1.568 seconds)

2)关闭本地模式,并执行查询语句

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

hive (default)> select * from score cluster by s_id;

18 rows selected (11.865 seconds)

9.2 表的优化

9.2.1 Join

Join原则:

1)小表Join大表,

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

select  count(distinct s_id)  from score;

select count(s_id) from score group by s_id; 在map端进行聚合,效率更高

select count(1) from (select s_id from score group by s_id) t;

 

 

2)多个表关联时,最好分拆成小段,避免大sql(无法控制中间Job)

3)大表Join大表

(1)空KEY过滤

有时join超时是因为某些key对应的数据太多,而相同key对应的数据都会发送到相同的reducer上,从而导致内存不够。此时我们应该仔细分析这些异常的key,很多情况下,这些key对应的数据是异常数据,我们需要在SQL语句中进行过滤。例如key对应的字段为空,操作如下:

环境准备:

create table ori(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';

 

load data local inpath '/export/servers/hivedatas/hive_big_table/*' into table ori;

load data local inpath '/export/servers/hivedatas/hive_have_null_id/*' into table nullidtable;

 

不过滤:

INSERT OVERWRITE TABLE jointable

SELECT a.* FROM nullidtable a JOIN ori b ON a.id = b.id;

结果:

No rows affected (152.135 seconds)

 

过滤:

INSERT OVERWRITE TABLE jointable

SELECT a.* FROM (SELECT * FROM nullidtable WHERE id IS NOT NULL) a JOIN ori b ON a.id = b.id;

结果:

No rows affected (141.585 seconds)

 (2)空key转换

有时虽然某个key为空对应的数据很多,但是相应的数据不是异常数据,必须要包含在join的结果中,此时我们可以表a中key为空的字段赋一个随机的值,使得数据随机均匀地分布到不同的reducer上。例如:

不随机分布:

set hive.exec.reducers.bytes.per.reducer=32123456;

set mapreduce.job.reduces=7;

INSERT OVERWRITE TABLE jointable

SELECT a.*

FROM nullidtable a

LEFT JOIN ori b ON CASE WHEN a.id IS NULL THEN 'hive' ELSE a.id END = b.id;

No rows affected (41.668 seconds)   52.477

结果:这样的后果就是所有为null值的id全部都变成了相同的字符串,及其容易造成数据的倾斜(所有的key相同,相同key的数据会到同一个reduce当中去)

 (3)空key打散

为了解决这种情况,我们可以通过hiverand函数,随机地给每一个为空的id赋上一个随机值,这样就不会造成数据倾斜

随机分布:

set hive.exec.reducers.bytes.per.reducer=32123456;

set mapreduce.job.reduces=7;

INSERT OVERWRITE TABLE jointable

SELECT a.*

FROM nullidtable a

LEFT JOIN ori b ON CASE WHEN a.id IS NULL THEN concat('hive', rand()) ELSE a.id END = b.id;

No rows affected (42.594 seconds)

 

 

 

 

4)案例实操

(0)需求:测试大表JOIN小表和小表JOIN大表的效率 (新的版本当中已经没有区别了,旧的版本当中需要使用小表)

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

create table bigtable(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 smalltable(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 jointable2(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 '/export/servers/hivedatas/big_data' into table bigtable;

hive (default)>load data local inpath '/export/servers/hivedatas/small_data' into table smalltable;

(3)关闭mapjoin功能(默认是打开的)

set hive.auto.convert.join = false;

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

INSERT OVERWRITE TABLE jointable2

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;

Time taken: 67.411 seconds 

 

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

INSERT OVERWRITE TABLE jointable2

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;

Time taken: 69.376seconds

可以看出大表join小表或者小表join大表,就算是关闭map端join的情况下,在新的版本当中基本上没有区别了(hive为了解决数据倾斜的问题,会自动进行过滤)

9.2.2 MapJoin

如果不指定MapJoin或者不符合MapJoin的条件,那么Hive解析器会将Join操作转换成Common Join,即:在Reduce阶段完成join。容易发生数据倾斜。可以用MapJoin把小表全部加载到内存在map端进行join,避免reducer处理。

1)开启MapJoin参数设置:

(1)设置自动选择Mapjoin

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

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

set hive.mapjoin.smalltable.filesize=25123456;

2)MapJoin工作机制

 

首先是Task A,它是一个Local Task(在客户端本地执行的Task),负责扫描小表b的数据,将其转换成一个HashTable的数据结构,并写入本地的文件中,之后将该文件加载到DistributeCache中。

接下来是Task B,该任务是一个没有Reduce的MR,启动MapTasks扫描大表a,在Map阶段,根据a的每一条记录去和DistributeCache中b表对应的HashTable关联,并直接输出结果。

由于MapJoin没有Reduce,所以由Map直接输出结果文件,有多少个Map Task,就有多少个结果文件。

案例实操:

(1)开启Mapjoin功能

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

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

INSERT OVERWRITE TABLE jointable2

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;

Time taken: 31.814 seconds

 

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

INSERT OVERWRITE TABLE jointable2

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;

Time taken: 28.46 seconds

9.2.3 Group By

默认情况下,Map阶段同一Key数据分发给一个reduce,当一个key数据过大时就倾斜了。

    并不是所有的聚合操作都需要在Reduce端完成,很多聚合操作都可以先在Map端进行部分聚合,最后在Reduce端得出最终结果。

1)开启Map端聚合参数设置

       (1)是否在Map端进行聚合,默认为True

set hive.map.aggr = true;

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

    set hive.groupby.mapaggr.checkinterval = 100000;

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

    set hive.groupby.skewindata = true;

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

9.2.4 Count(distinct)

数据量小的时候无所谓,数据量大的情况下,由于COUNT DISTINCT操作需要用一个Reduce Task来完成,这一个Reduce需要处理的数据量太大,就会导致整个Job很难完成,一般COUNT DISTINCT使用先GROUP BY再COUNT的方式替换:

环境准备:

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

 

load data local inpath '/home/admin/softwares/data/100万条大表数据(id除以10取整)/bigtable' into table bigtable;

 

set hive.exec.reducers.bytes.per.reducer=32123456;

SELECT count(DISTINCT id) FROM bigtable;

结果:

c0

10000

Time taken: 35.49 seconds, Fetched: 1 row(s)

可以转换成:

set hive.exec.reducers.bytes.per.reducer=32123456;

SELECT count(id) FROM (SELECT id FROM bigtable GROUP BY id) a;

结果:

Stage-Stage-1: Map: 1  Reduce: 4   Cumulative CPU: 13.07 sec   HDFS Read: 120749896 HDFS Write: 464 SUCCESS

Stage-Stage-2: Map: 3  Reduce: 1   Cumulative CPU: 5.14 sec   HDFS Read: 8987 HDFS Write: 7 SUCCESS

_c0

10000

Time taken: 51.202 seconds, Fetched: 1 row(s)

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

9.2.5 笛卡尔积

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

9.2.6 使用分区剪裁、列剪裁

在SELECT中,只拿需要的列,如果有,尽量使用分区过滤,少用SELECT *。

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

环境准备:

create table ori(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 bigtable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';

 

load data local inpath '/home/admin/softwares/data/加递增id的原始数据/ori' into table ori;

 

load data local inpath '/home/admin/softwares/data/100万条大表数据(id除以10取整)/bigtable' into table bigtable;

先关联再Where:

SELECT a.id

FROM bigtable a

LEFT JOIN ori b ON a.id = b.id

WHERE b.id <= 10;

正确的写法是写在ON后面:先Where再关联

SELECT a.id

FROM ori a

LEFT JOIN bigtable b ON (a.id <= 10 AND a.id = b.id);

或者直接写成子查询:

SELECT a.id

FROM bigtable a

RIGHT JOIN (SELECT id

FROM ori

WHERE id <= 10

) b ON a.id = b.id;

9.2.7 动态分区调整

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

 

说白了就是以第一个表的分区规则,来对应第二个表的分区规则,将第一个表的所有分区,全部拷贝到第二个表中来,第二个表在加载数据的时候,不需要指定分区了,直接用第一个表的分区即可

 

 

1)开启动态分区参数设置

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

set hive.exec.dynamic.partition=true;

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

set hive.exec.dynamic.partition.mode=nonstrict;

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

set  hive.exec.max.dynamic.partitions=1000;

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

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

(5)整个MR Job中,最大可以创建多少个HDFS文件。

       在linux系统当中,每个linux用户最多可以开启1024个进程,每一个进程最多可以打开2048个文件,即持有2048个文件句柄,下面这个值越大,就可以打开文件句柄越大

set hive.exec.max.created.files=100000;

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

set hive.error.on.empty.partition=false;

2)案例实操

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

(1)准备数据原表

create table ori_partitioned(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string)

PARTITIONED BY (p_time bigint)

row format delimited fields terminated by '\t';

 

load data local inpath '/export/servers/hivedatas/small_data' into  table ori_partitioned partition (p_time='20111230000010');

 

load data local inpath '/export/servers/hivedatas/small_data' into  table ori_partitioned partition (p_time='20111230000011');

(2)创建分区表

create table ori_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';

(3)分析

如果按照之前介绍的往指定一个分区中Insert数据,那么这个需求很不容易实现。这时候就需要使用动态分区来实现。

set hive.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;

 

INSERT overwrite TABLE ori_partitioned_target PARTITION (p_time)

SELECT id, time, uid, keyword, url_rank, click_num, click_url, p_time

FROM ori_partitioned;

注意:在PARTITION (month,day)中指定分区字段名即可;

在SELECT子句的最后几个字段,必须对应前面PARTITION (month,day)中指定的分区字段,包括顺序。

查看分区

hive> show partitions ori_partitioned_target;

OK

p_time=20111230000010

p_time=20111230000011

9.2.8 分桶

参见分桶表

转载于:https://www.cnblogs.com/mediocreWorld/p/11068390.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值