欢迎投稿

今日深度:

Hive优化,

Hive优化,


Hive优化

Hive优化思想:

Hive是将符合SQL语法的字符串解析生成可以在Hadoop上执行的MapReduce的工具,所以学习MapReduce的原理对我们使用hive,优化hive有很大的帮助。

使用Hive尽量按照分布式计算的一些特点来设计sql,可以提升效率。

Hive性能优化时,把HiveQL当做M/R程序来读,即从M/R的运行角度来考虑优化性能,从更底层思考如何优化运算性能,而不仅仅局限于逻辑代码的替换层面。

前提:

(1)熟知业务要求

(2)熟悉业务数据(业务中各种数据类型,数据大小)

(3)了解数据分布,可以解决数据倾斜问题,想要解决特定业务场景下的问题,还必须熟悉业务逻辑。

例如:通过set hive.groupby.skewindata=true;解决数据倾斜问题,这是使用的算法优化,但算法优化有时不能适应特定业务背景,开发人员了解业务,了解数据,可以通过业务逻辑精确有效的解决数据倾斜问题。

一.使用合适的数据类型和存储格式

1.使用合适的数据类型

  Hive 提供了基本数据类型和复杂数据类型,在特定的业务场景下,使用相应的数据类型可以减小内存开销,提升效率。

  • 整型
    • TINYINT : 微整型,只占用1个字节,只能存储0-255的整数。
    • SMALLINT: 小整型,占用2个字节,存储范围–32768 到 32767。
    • INT: 整型,占用4个字节,存储范围-2147483648到2147483647。
    • BIGINT– 长整型,占用8个字节,存储范围-2^63到2^63-1。
  • 字符串型
    • STRING: 不设定长度
    • VARCHAR: (从Hive0.12.0开始支持)字符数范围1-65535,长度不定字符串
    • CHAR: (从Hive0.13.0开始支持)最大字符数:255,长度固定字符串

从以上数据类型的大小可以看出,在熟知业务数据以及业务要求之后,我们可以选择合适的数据类型。CHAR类型在字符串长度小于255的时候,会用空格补充,造成资源浪费,在对CHAR类型的数据运算的时候也会增大内存开销。

在我们QILAP项目中,学生姓名,班级名称,题目所属学科,题目所属阶段,题目难度等字段,由于我们已经知道字符串大概长度,所以全都可以使用VARCHAR类型。由于学生分数最大为100分,所以可以使用TINYINT类型。

注:

Hive-0.12.0版本引入了VARCHAR类型,VARCHAR类型使用长度指示器(1到65355)创建,如果一个字符串值转换为或者被赋予一个varchar值,其长度超过了长度指示器则该字符串值会自动被截断。

Hive-0.13.0版本引入了CHAR类型,CHAR类型与VARCHAR类型相似,但拥有固定的长度,也就是如果字符串长度小于指示器的长度则使用空格填充。CHAR类型的较大长度为255。String 类型 与Java中的类型很相似

2.存储格式(详情见MR教案-MR高阶知识)

① TEXTFILE //文本,默认值

② SEQUENCEFILE // 二进制序列文件

③ RCFILE //列式存储格式文件 Hive0.6以后开始支持

④ ORC //列式存储格式文件,比RCFILE有更高的压缩比和读写效率,Hive0.11以后开始支持

⑤ PARQUET //列出存储格式文件,Hive0.13以后开始支持

二.Hive的具体优化

由于hive底层使用的mapreduce计算框架,因此也会衍生出很多问题:

 

 

 

下面从多个角度对Hive进行性能优化:

1.map/reduce数目优化

  • Map阶段优化

通过配置参数,设置合适的map数量:

通过调整这三个参数的大小来调整map的数量:

减小参数可以增加map数,增大参数可以减少map数。

 

注: (1)直接调整mapred.map.tasks这个参数是没有效果的。

(2)这个优化只是针对Hive 0.9版本。

  • reduce阶段优化

Reduce阶段优化的主要工作也是选择合适的reduce task数量, 与map优化不同的是,reduce优化时,可以直接设置mapred.reduce.tasks参数从而直接指定reduce的个数。

根据输入的数据量大小来决定Reduce数量,数据量=reduce个数*每个reduce处理的数据大小。

如果我们将hive.exec.reducers.max设置到最大1009个,通过调整每个reduce处理的数据大小(hive.exec.reducers.bytes.per.reducer)就可以调整reduce的数量。

注:

(1)Reduce的个数对整个作业的运行性能有很大影响。如果Reduce设置的过大,那么将会产生很多小文件,对NameNode会产生一定的影响,而且整个作业的运行时间未必会减少;如果Reduce设置的过小,那么单个Reduce处理的数据将会加大,很可能会引起OOM异常

 

2.列裁剪

列裁剪就是只查询我们需要的列,而忽略不需要的列。我们在使用hive的时候,很多时候都用到了列裁剪,只是不知道这种方式叫列裁剪而已。

    例:表T有5列(a,b,c,d,e)执行sql语句select a,c,d from T;

    Hive只查询了a,c,d列,忽略了b,e列,这样降低了读取开销。

列裁剪所对应的参数项:hive.optimize.cp=true(默认值为真)。

3.设置分区

分区:每个表可以有一个或多个分区,用于确定数据的存储方式。只能在表的相关分区上运行该查询,从而显着加快分析速度。但请注意,仅仅因为分区名为2018-12-6并不意味着它包含该日期的全部,保证分区名称和数据内容之间的关系由用户的决定; 只是为方便起见,分区以日期命名;分区列是虚拟列,这些抽象允许系统在查询处理期间修剪大量数据,从而加快查询执行速度。

 

注:

·  在Hive Select查询中一般会扫描整个表内容,会消耗很多时间做没必要的工作。有时候只需要扫描表中关心的一部分数据,因此建表时引入了partition概念。

·  分区表指的是在创建表时指定的partition的分区空间。

·  如果需要创建有分区的表,需要在create表的时候调用可选参数partitioned by

4.join优化

在使用写有 Join 操作的查询语句时有一条原则:应该将条目少的表/子查询放在 Join 操作符的左边。原因是在 Join 操作的 Reduce 阶段,位于 Join 操作符左边的表的内容会被加载进内存,将条目少的表放在左边,可以有效减少发生 OOM 错误的几率。对于一条语句中有多个 Join 的情况,如果 Join 的条件相同,就将小表放在左边,前提条件是小表中包含了我们需要的全部字段。

hive中的join操作的关键字必须在on中指定,不能再where中指定,不然会先做笛卡尔积再过滤;

如果 Join 的 key 相同,不管有多少个表,都会则会合并为一个 Map-Reduce

在join前过滤,减小了join的时候操作的数据量

例:

select

    ...

from

    A

join

    B

on

    A.key = B.key

where

     A.id>10

     and B.id<10

     and A.dt='20120417'

     and B.dt='20120417';

改写为:

select

    ....

from

(

select

    ....

from

     A

where

    dt='201200417'                                

and

    id>10

) a

join

(

select

    ....

from

    B     

where

    dt='201200417'

and

    id < 10  

) b

on

    a.key = b.key;

mapJoin的主要意思就是,当链接的两个表是一个比较小的表和一个特别大的表的时候,我们把比较小的table直接放到内存中去,然后再对比较大的表格进行map操作。join就发生在map操作的时候,每当扫描一个大的table中的数据,就要去去查看小表的数据,哪条与之相符,继而进行连接。这里的join并不会涉及reduce操作。map端join的优势就是在于没有shuffle。

在实际的应用中,我们这样设置:set hive.auto.convert.join=true; 

这样设置hive就会自动的识别比较小的表,继而用mapJoin来实现两个表的联合。看看下面的两个表格的连接。这里的dept相对来讲是比较小的。我们看看会发生什么,如图所示:

这里的第一句话就是运行本地的map join任务,继而转存文件到XXX.hashtable下面,在给这个文件里面上传一个文件进行map join,之后才运行了MR代码去运行计数任务。说白了,在本质上mapjoin根本就没有运行MR进程,仅仅是在内存就进行了两个表的联合。具体运行如下图:

当对3个或者更多个表进行join连接时,如果每个on子句都使用相同的连接键的话,那么只会产生一个MapReduce job。

 

5.根据数据大小采用不同的hive模式

Hive,Map-Reduce和Local-Mode

 Hive编译器为大多数查询生成map-reduce作业。然后将这些作业提交给变量指示的Map-Reduce集群:  mapred.job.tracker

 虽然这通常指向具有多个节点的map-reduce集群,但Hadoop还提供了一个漂亮的选项,可以在用户的​​工作站上本地运行map-reduce作业。这对于在小型数据集上运行查询非常有用 - 在这种情况下,本地模式执行通常比将作业提交到大型集群要快得多。从HDFS透明地访问数据。相反,本地模式仅使用一个reducer运行,并且处理较大的数据集可能非常慢。

 从版本0.7开始,H​​ive完全支持本地模式执行。要启用此功能,用户可以启用以下选项:  hive> SET mapreduce.framework.name = local;

 此外,mapred.local.dir应指向在本地计算机上有效的路径(例如/tmp/<username>/mapred/local)。(否则,用户将获得分配本地磁盘空间的异常。)

 从版本0.7开始,H​​ive还支持一种模式,可以自动在本地模式下运行map-reduce作业。

相关的选项有:hive.exec.mode.local.auto,hive.exec.mode.local.auto.inputbytes.max和hive.exec.mode.local.auto.tasks.max: 

hive> SET hive.exec.mode.local.auto = false;

请注意,默认情况下禁用此功能。如果启用,Hive将分析查询中每个map-reduce作业的大小,如果满足以下阈值,则可以在本地运行它:

1.作业的总输入大小低于:(hive.exec.mode.local.auto.inputbytes.max默认为128MB)

2.map-tasks的总数小于:(hive.exec.mode.local.auto.tasks.max默认为4)

3.所需的reduce任务总数为1或0。

    因此,对于小数据集的查询,或者对于具有多个map-reduce作业的查询,其中后续作业的输入要小得多(由于先前作业中的减少/过滤),可以在本地运行作业。

    请注意,Hadoop服务器节点和运行Hive客户端的计算机的运行时环境可能存在差异(因为不同的jvm版本或不同的软件库)。在本地模式下运行时,这可能会导致意外的行为/错误。另请注意,本地模式执行是在一个单独的子jvm(Hive客户端)中完成的。如果用户愿意,可以通过该选项控制该子jvm的最大内存量hive.mapred.local.mem。默认情况下,它设置为零,在这种情况下,Hive允许Hadoop确定子jvm的默认内存限制。

6.数据倾斜问题

表现:任务进度长时间维持在99%(或100%),查看任务监控页面,发现只有少量(1个或几个)reduce子任务未完成。因为其处理的数据量和其他reduce差异过大。单一reduce的记录数与平均记录数差异过大,通常可能达到3倍甚至更多。 最长时长远大于平均时长。

原因:

关键词

情形

造成后果

join

其中一个表较小,但是某一个key较多

分发到某一个或几个Reduce上的数据远高于平均值

join

大表与大表,但是分桶的判断字段0值或空值过多

这些空值都由一个reduce处理

group by

group by 维度过小,某值的数量过多

处理某值的reduce非常耗时

count (distinct)

某特殊值过多

处理此特殊值reduce耗时

 

 

设置hive.groupby.skewindata为true和hive.map.aggr为true避免数据倾斜。

设置hive.map.aggr为true:在map中会做部分聚集操作,效率更高但需要更多的内存。

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

      

 

原理:

(1)会生成两个job来执行group by,第一个job中,各个map是平均读取分片的,在map阶段对这个分片中的数据根据group by 的key进行局部聚合操作,这里就相当于Combiner操作。

(2)在第一次的job中,map输出的结果随机分区,这样就可以平均分到reduce中

(3)在第一次的job中,reduce中按照group by的key进行分组后聚合,这样就在各个reduce中又进行了一次局部的聚合。

(4)因为第一个job中分区是随机的,所有reduce结果的数据的key也是随机的,所以第二个job的map读取的数据也是随机的key,所以第二个map中不存在数据倾斜的问题。

(5)在第二个job的map中,也会进行一次局部聚合。

(6)第二个job中分区是按照group by的key分区的,这个地方就保证了整体的group by没有问题,相同的key分到了同一个reduce中。

(7)经过前面几个聚合的局部聚合,这个时候的数据量已经大大减少了,在最后一个reduce里进行最后的整体聚合

 

 

 

 

注:

设置set hive.groupby.skewindata=true ,默认该参数的值为false,表示不启用,要启用时,可以set hive.groupby.skewindata=ture;进行启用。

当启用时,能够解决数据倾斜的问题,但如果要在查询语句中对多个字段进行去重统计时会报错。

hive> set hive.groupby.skewindata=true;

hive> select count(distinct id),count(distinct x) from test;

FAILED: SemanticException [Error 10022]: DISTINCT on different columns not supported with skew in data

下面这种方式是可以正常查询

hive>select count(distinct id, x) from test;

7.合并小文件

文件数目小,容易在文件存储端造成瓶颈,给HDFS 带来压力,影响处理效率。对此,可以通过合并Map和Reduce的结果文件来消除这样的影响。

hive.merg.mapfiles=true:合并map输出文件
hive.merge.mapredfiles=false:合并reduce输出文件
hive.merge.size.per.task=256*1000*1000:合并文件的大小
hive.mergejob.maponly=true:如果支持CombineHiveInputFormat则生成只有Map的任务执行merge
hive.merge.smallfiles.avgsize=16000000:文件的平均大小小于该值时,会启动一个MR任务执行merge。

8.排序优化

order by : 对查询结果进行全局排序,消耗时间长。需要 set hive.mapred.mode=nostrict

sort by : 局部排序,并非全局有序,提高效率。

9.从程序角度优化

想要详细了解如何从程序角度优化:

请参考:https://www.cnblogs.com/smartloli/p/4356660.html

 

 

 

参考自:

https://blog.csdn.net/chybin500/article/details/80988089

https://www.cnblogs.com/smartloli/p/4356660.html

https://www.cnblogs.com/sandbank/p/6408762.html

https://blog.csdn.net/mrlevo520/article/details/76339075/

 

www.htsjk.Com true http://www.htsjk.com/hive/41310.html NewsArticle Hive优化, Hive优化 Hive优化思想: Hive是将符合SQL语法的字符串解析生成可以在Hadoop上执行的MapReduce的工具,所以学习MapReduce的原理对我们使用hive,优化hive有很大的帮助。 使用Hive尽量按...
相关文章
    暂无相关文章
评论暂时关闭