Hive的优化历程

公司的系统想要转型,由我和项目经理两个人来完成从传统的数据库向HIVE+HADOOP_+SPARK,用以满足日益膨胀的大量数据。
对于将数据存储在Hive,进行了以下的优化:
1,Hive的引擎目前为止有三种,分别为MR,TEZ,SPRAK.由于公司用的是Hive1.2.1,spark是 老版本1.6.2,我查了hive on spark 的网页后发现这个hive version 不支持我目前这个版本的 spark,因此转向研究这两种引擎的区别.

beeline->set hive.execution.engine=tez;
beeline->set hive.execution.engine=mr;
beeline->set hive.execution.engine=spark;

研究前,我对tez并不了解,因此查看官网的文档是最好的入门,这里应该查看Hive on TEZ
,来初步了解Hive on TEZ的优势和特点.

个人的总结如下:
(1)当引擎为MR的时候,如果有稍微复杂的操作,比如内外连接,MR会转化为多个MR,当上一组MR执行完后下一组MR会继续执行,每一次Maper都会从HDFS读取文件,而Reducer会从HDFS写入文件,并且Reducer也会从一到多个Maper读取文件,造成频繁的IO开销。如果采用引擎为TEZ的话,结构可以转化成MRR,即通过一个或多个Mapper之后,进入一个或多个Reducer,当Reducer结束之后,不写入hdfs,而是直接下得到的数据转入给下一组Reducer,重复知道最后得到结果并将其写入hdfs。避免了过多的IO流
(2)TEZ可以将小的数据集放入内存中进行运算,这一点MR做不到

2,要优化查询语句,这一点无论什么数据平台都是通用的

(1) 关键词join的优化
当使用 表a join 表 b的时候,hive会默认将左边的表缓存起来,对右边的表进行扫描,基于这样的机制,在使用
join 关键字的时候,将小表放在左边对于提高性能有一定的好处;
注意的是:
这里写图片描述

对于多个表的join,如果所有的表中只有一张是小表,可以采取以下的优化:
Map-side join, 该功能可以将小表缓存的内存中,在大表进行Map操作的时候,遍历内存中的小表进行逐一匹配,这样可以虽然需要将小表分发到各个Map节点,但是却可以省去Reducer阶段

一般的做法是进行如下的设置:
beeline–> set hive.auto.convert.join= true;
来让hive自动实现,同时可以在hive-site.xml里面设置对于小表的定义
hive.mapjoin.smalltable.filesize=25000000(单位字节)

注意:本操作对右链接和全外连接不适合用

(2)从同个表中截取数据到不同表的优化
Hive的优化历程_第1张图片

(3)order by 与sort by +distribute by

order by 全局排序,最终会将所有数放到一个ruducer进行排序,数据量大的时候就扑街了

sort by 局部排序,会在每个reducer内进行排序,在Maper阶段完成之后,进行hash操作,将键值均匀的分发到Reducer上去,那么如果我们能保证分发到的每一台Recuder上的都是同一个key(我们要select的那个值)的话,那么在每一台Recuder上进行局部的排序同样也能实现order by的功能,这个时候就用distribute by key 来将目标的key分发到不同的Reducer上

Hive的优化历程_第2张图片

3,利用analyse

官网对于analyze的解析

总的来说,使用analyze table 可以获取表的一些基本信息,如
numPartitions,numFiles,totalSize,rawDataSize,numRows,
有助于系统生成更好的查询计划。

4,将文件存储格式修改为ORC
ORC文件的概念可以参考这里
简单来说ORC是Hive原生的存储文件的格式,通过压缩,索引进一步优化数据存储加快数据查询速度。
创建一个以 ORC为格式的文件如下:
create table if not exists test_orc(
name string,
gender string,
cnt BIGINT
)STORED AS ORC;
或者是压缩的存储如下:
create table if not exists test_orc(
name string,
gender string,
cnt BIGINT
)STORED AS ORC tblproperities(“orc.compress”=”SNAPPY”);
其中(“orc.compress”=”SNAPPY”)是设置压缩的方式,也可以设置为(“orc.compress”=”ZLIB”)

值得注意的是,由于创建的是ORC文件,所以不能直接的使用load data的cmd将txt文件插入到数据格式为ORC的表中,只能:
**将数据导入临时普通表:
LOAD DATA INPATH ‘/tmp/orc.txt’ OVERWRITE INTO TABLE test_orc_tmp;
将临时普通表的数据插入到ORC表:
INSERT INTO TABLE test_orc SELECT * FROM test_orc_tmp;**

5,使用VECTORIZATION
关于VECTORIZATION
简单来说:每次查询都执行一块1024行而不是一行,这样做符合计算机设计底层原理,提高了查询的速率
设置为:
set hive.vectorized.execution.enabled = true;
set hive.vectorized.execution.reduce.enabled = true;

注意,只要当表的存储格式为ORC的时候才可以采用这种矢量查询方式。

6,利用并行执行

想hive提交一个job的时候,会根据不同的功能分成不同的stage,可以通过 explain sql 来看,
如果每个stage之间没有依赖关系的话,可以设置并行执行,可以理解为多线程吧,但是这样会消耗更多资源。
比较好的例子就是union的时候可以并行执行,
设置如下:

// 开启任务并行执行
set hive.exec.parallel=true;
// 同一个sql允许并行任务的最大线程数
set hive.exec.parallel.thread.number=8;

关于这点,可以看这个仁兄的经验

7,利用shell控制数据倾斜
在每天走的ETL的流程中,经常在前面是关联许多表来形成一个大的数据集,也有一些比较大点的表十几G和一些上千行的小表进行关联的操作,由于join 的key分布不均匀,有些几十万次有些几十次,造成了数据处理缓慢,设置负载均衡也花费了二十多分钟的时候,影响了整体数据处理的流程。
因为用shell脚本将该HQL语句进行分层处理,处理后运行时间五分多钟,加上union各个部分的集合,不到八分钟。

需要处理的语句如下: 为了方便记录简化了语句
hql:
select row_number() over() as row_id ,xxx,xxx,xxx from t_stg00 a,t_rule_main b
where a.balance_sheet_point_1= b.balance_sheet_pointer_lo
and b.balance_sheet_point_lo=b,balance_sheet_point_1_hi
and (a.asset=b.asset or b.asset=’*’)
–and b.row_id between startrowand s t a r t r o w a n d {end_row}; 在这里控制小表进行join的行数
and a.balance_sheet_pointer_lo in ($joinKeyList); –指定joinKey 的集合,joinKey是造成数据倾斜的数据

接着写个主要的shell脚本来处理上面 and b.balance_sheet_pointer_lo in ($joinKeyList);的分布
logic 是先分析统计join 的key 分布,并且将分布存储在一个file中
格式为 joinKey count
然后遍历这个file ,如果count>50万次,则单独开一个线程来运行,如果count<50W,则继续遍历下一行并且count的数据继续增加知道超过50W则再次开线程来处理

但这里有个问题,我们的统计分析是统计分析大表,需要进行count(distinct)的处理,因此会花不少时间,但是由于业务数据的波动范围不大,因为不用每次都分析数据,只有在数据有必要的时候分析一次并且存入上面所说的要遍历的file即可。但是这样子做出来的脚本和分析思路可以应用到有相似需求的业务。

具体实现:
(1)先对大表进行统计并且存入到一个file中
beeline -u jdbcurlshowHeader=falsee"selectbalancesheetpointerlo,count(distinct(1))fromtstg00;"> j d b c u r l − − s h o w H e a d e r = f a l s e − e " s e l e c t b a l a n c e s h e e t p o i n t e r l o , c o u n t ( d i s t i n c t ( 1 ) ) f r o m t s t g 00 ; "> file_path
#统计数据存入文件中为了下面控制线程数量用

(2)

key="key"
count=0
subtaskNum=0
declare -a batch_line ##declare a array named batch_line
while read -ra batchline ## loop the file and read one line and split this line by tab,then put the datas 
                         ##array batch_line
    key=$batch_line[0]
    count=$batch_line[1]

    $subtaskNum= `expr $subtaskNum+1`
    ####
    ##if match to trigger a proces
    ####
    nohub ./appl/subtask$subtaskNum.sh $joinKeyList >>$log_file 2>&1 &
do

done < $path_of_file ##tell shell which file u want to loop read 

note:
用nohup运行命令可以使命令永久的执行下去,和用户终端没有关系,例如我们断开SSH连接都不会影响他的运行,注意了nohup没有后台运行的意思;&才是后台运行

&是指在后台运行,但当用户推出(挂起)的时候,命令自动也跟着退出

你可能感兴趣的:(Hive)