大数据之Hadoop数据仓库框架Hive(五-4):Hive 优化

16 篇文章 2 订阅

引言

\quad \quad Hive的底层是MapReduce,当数据表太大时,往往可以通过并行来提高效率,比如通过分区实现运行多个reduce,可是如果处理不当则容易引发数据倾斜,从而导致效率较低,这就涉及到Hive 的优化。Hive的优化可以从以下几个方面进行

1、数据存储格式

Hive文件存储格式有以下几种:
在这里插入图片描述

  • TextFile是Hive默认的文件存储格式,存储方式为行存储;使用这种方式,hive不会对数据进行切分,从而无法对数据进行并行操作。

  • sequencefile二进制文件,以<key,value>的形式序列化到文件中;存储方式:行存储;

  • refile读取需要的列只需要读取每个row group 的头部定义;读取全量数据的操作 性能可能比sequencefile没有明显的优势

  • orc存储方式:数据按行分块,每块按照列存储;压缩快 快速列存取;效率比rcfile高,是rcfile的改良版本。

  • parquet类似于orc,相对于orc文件格式,hadoop生态系统中大部分工程都支持parquet文件。

比对三种主流的文件存储格式TEXTFILE 、ORC、PARQUET
压缩比:ORC > Parquet > textFile(textfile没有进行压缩)
查询速度:三者几乎一致
HDFS上显示的是原来的文件名,如果压缩的话,使用类似于000000_0的文件名
  • 对于大量数据的情况下,工作中常用orc 存储格式
  • 我们在建表的时候可以通过命令stored as指定文件存储格式为orc,从而提高查询效率。
create table tableName(字段名称 字段类型 [comment '中文注释说明'],字段名称 字段类型, ....)
row format delimited fields terminated by 'char分割符即列分割符'
lines terminated by '行分割符'
stored as orc;

2、MapReduce优化

如何设置Map数、Reduce数很关键,因为设置的不当,就容易造成数据倾斜。

2.1 Map端优化

1、合理设置Map数

(1)通常情况下,作业会通过input的目录产生一个或者多个map任务。

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

(2)是不是map数越多越好?

  • 答案是否定的。如果一个任务有很多小文件(远远小于块大小128m),则每个小文件也会被当做一个块,用一个map任务来完成,而一个map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的map数是受限的。

(3)是不是保证每个map处理接近128m的文件块,就高枕无忧了?

  • 答案也是不一定。比如有一个127m的文件,正常会用一个map去完成,但这个文件只有一个或者两个小字段,却有几千万的记录,如果map处理的逻辑比较复杂,用一个map任务去做,肯定也比较耗时。

针对上面的问题2和3,我们需要采取两种方式来解决:即减少map数和增加map数;

2、小文件进行合并——减少map数

  • 在Map执行前即Map输入合并小文件:
set hive.input.format= org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;

3、复杂文件增加Map数

  • 当input的文件都很大,任务逻辑复杂,map执行非常慢的时候,可以考虑增加Map数,来使得每个map处理的数据量减少,从而提高任务的执行效率。

  • 增加map的方法为:根据

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

2.2 Reduce端优化

  • 合理控制reducer数量
  • 通过set mapreduce.job.reduces=n直接设置reducer数量,这个方法效率高

3、裁剪

\quad \quad 尽可能早地过滤掉尽可能多的数据量,避免大量数据流入外层SQL。

3.1 列裁剪

  • 只获取需要的列的数据,减少数据输入。

  • 少用select *

3.2 分区裁剪

  • 分区在hive实质上是目录,分区裁剪可以方便直接地过滤掉大部分数据。

  • 尽量使用分区过滤,也就是尽量先将所需的数据过滤出来,然后再进行其他

比如:有两个表:orders表记录了订单数,用户id;trains表记录了周几下单,用户id;先统计周一的下单数。

方式一:先关联再过滤,不推荐

select count(*) order_cnt
from orders ord 
inner join trains tr
on ord.order_id=tr.order_id
where order_dow=1;

方式二:先过滤再关联,推荐

select count(*) order_cnt
from orders ord 
inner join trains tr
on (ord.order_id=tr.order_id and order_dow=1 );

或者直接写成子查询

select count(*) order_cnt
from (select * from orders where order_dow=1)  ord 
inner join trains tr
on ord.order_id=tr.order_id;

4、join 优化

4.1 调整on后面的约束条件

  • 一个MR job a,b,c
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 a,b,b,c
select a.val,b.val,c.val
from a
join b on (a.key = b.key1)
join c on (c.key = b.key2)

4.2 表的连接顺序

  • 小表 join 大表
  • 按照JOIN顺序中的最后一个表应该尽量是大表

4.3 Mapjoin

  • MapJoin顾名思义,就是在Map阶段进行表之间的连接。而不需要进入到Reduce阶段才进行连接。这样就节省了在Shuffle阶段时要进行的大量数据传输。从而起到了优化作业的作用。

  • MapJoin适用于有一张十分小的表和一张大的表的场景,会把小表全部加载到内存中,在map阶段直接拿另外一个表的数据和内存中表数据做匹配,这样的话就可以在MapTask阶段将非常小的那几张表加载进内存,提前处理业务从而减少Reduce端的压力,以减少数据倾斜。

  • Mapjoin工作机制
    假设a表为一张大表,b为小表,
    在这里插入图片描述

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

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

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

语法

select 
/*+ MAPJOIN(aisles) */ a.aisle as aisle_name, b.product_name
from aisles a 
inner join products b
on a.aisle_id=b.aisle_id
limit 10;

其中,b表是小表

5、笛卡尔积

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

6、count(distinct)—>group by优化

  • 数据量小的时候无所谓,数据量大的情况下,由于count(distinct) 操作需要用一个reduce Task来完成,这一个Reduce需要处理的数据量太大,就会导致整个Job很难完成,一般count(distinct)使用先group by 再count的方式替换

  • 注意:在工作中针对数据去重 能使用group by 就不使用distinct

    • 使用group by 多个reduce进行处理
    • distinct 所有数据在一个reduce中进行处理
SELECT count(DISTINCT id) FROM bigtable;

可以转化为

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

7、压缩

压缩:减小数据的小大和数据磁盘读取时间

  • map输出压缩
#设置为true为激活map输出数据压缩功能,默认是false,没有开启
set mapreduce.map.output.compress=true;
#设置map输出数据的压缩算法
set mapreduce.map.output.compress.codec= org.apache.hadoop.io.compress.SnappyCodec;
  • 中间数据压缩:对hive查询多个job之间的数据进行压缩
#设置为true为激活中间数据压缩功能,默认是false,没有开启
set hive.exec.compress.intermediate=true;
#设置中间数据的压缩算法
set hive.intermediate.compression.codec= org.apache.hadoop.io.compress.SnappyCodec;
set hive.intermediate.compression.type=BLOCK;
  • 结果数据压缩:reducer输出数据,结果数据作为其他查询任务数据源
set hive.exec.compress.output=true;
set mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.SnappyCodec;
set mapreduce.output.fileoutputformat.compress.type=BLOCK;

8、并行执行

  • 同步执行hive的多个阶段
  • hive可以在执行过程中,将一个查询转化为一个或多个阶段。某个特定的job可能包含众多的阶段,而这些阶段可能并非完全相互依赖的,也就是说可以并行执行的,这样可以使得整个任务的执行时间缩短,提高集群资源利用率。
--开启并行执行;默认false
set hive.exec.parallel=true;
--同一个sql允许最大并行度,默认为8。可设置
set hive.exec.parallel.thread.number=16;

9、动态分区优化

  • 当你所执行的任务,只需要从源数据的一部分进行查询,那么你就可以使用分区进行优化,然后在你所需要的那个分区内执行任务即可,而不是扫描整个数据表,这样就可以提高你的查询效率。
  • 使用动态分区,必须打开动态分区模式,并且设置分区模式为非严格模式
--1.打开动态分区模式:
set hive.exec.dynamic.partition=true;
--2.设置分区模式为非严格模式
set hive.exec.dynamic.partition.mode=nonstrict;

注意:以上设置,只在当前的会话窗口有效,关闭会话窗口,就不再有效了,需要重新设置

动态分区的话,不需要指定分区字段等于什么

  • 加载本地数据到动态分区表中
load data local inpath ‘文件路径’ into table 表名 partition (分区字段);
  • 加载本地数据到一个多分区的表中去
load data local inpath ‘/export/servers/hivedatas/score.csv’ into table score2 partition(year,month,day);
  • 也可以通过将查询结果加载到表中
insert overwrite table udata_partition partition (dt)
select 
user_id, item_id, rating
, to_date(from_unixtime(cast(`timestamp` as bigint), 'yyyy-MM-dd HH:mm:ss')) as res
from udata
where user_id='244';

应用场景:

  • 不确定分区数量,数据量也不是很大,使用动态分区,以及在插入数据的分区字段是不确定的情况下。
  • 实际工作中趋向于使用动态分区!!!

10、分桶优化

  • 分桶优化也是减小查询量,提高查询效率
  • 与分区不同的是,分区采用的分区字段不是原表中的字段名,比如年、月、日、城市这些常用的字段
  • 如果你想知道某个时间段的相关信息,那么你就可以采用分区
  • 但分桶采用的字段是原表中所拥有的字段

11、数据倾斜优化

原因:

  • 对数据进行聚合比如分组,key分布不均匀
  • 人为的建表疏忽
  • 业务数据特点
  • map和reduce个数设置不当

体现:

  • 任务进度长时间维持在99%,查看任务监控页面,发现只有少量reduce子任务未完成
  • 查看未完成的子任务,可以看到本地读写数据量积累非常大,通常超过10GB可以认定为发生数据倾斜。

万能解决方法

--有数据倾斜的时候进行负载均衡
set hive.groupby.skewindata = true;

11.1 异常值处理

现象:

  • 当异常值比如一些特殊符号之类的、空值比较多时,比方说在进行分桶的过程中,会将空值分到一起,也容易产生数据倾斜

处理:

  • 对异常值赋予随机变量来分散key,可以通过rand随机函数将为NULL值分散到不同的值上
case when uid is null then cast(rand(100)*10000 as int) else uid end
  • 将异常值单独拿出来处理,最后再合并进去

11.2 大小表关联

现象:

  • Hive在进行join时,因为join左边的表的会首先读入内存,如果左边的表的数据量过大,key比较集中,那么就容易导致数据倾斜

处理:

  • 小表 join 大表

未完待续

参考资料:

https://blog.csdn.net/weixin_43520450/article/details/107872251

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值