7、Hive 优化
- hdfs作为存储层
- MapReduce作为计算层
sql->mr
--查看执行计划,添加extended关键字可以查看更加详细的执行计划
explain [extended] query
1、抽象语法树
2、查询块
3、逻辑查询计划
4、物理查询计划
5、优化执行
执行模式
1、本地模式(开发,速度会有所提升): set hive.exec.mode.local.auto=true; 默认false --设置读取数据量的大小限制,超出使用集群模式 set hive.exec.mode.local.auto.inputbytes.max=128M 设计到的文件不能超过4个文件 2、并行模式 在SQL语句足够复杂的情况下,可能在一个SQL语句中包含多个子查询语句,且多个子查询语句之间没有任何依赖关系,此时,可以Hive运行的并行度 set hive.exec.parallel=true; set hive.exec.parallel.thread.number=8; 3、严格模式:充分利用Hive的某些特点 set hive.mapred.mode=strict; 注意:当设置严格模式之后,会有如下限制: (1)对于分区表,必须添加where对于分区字段的条件过滤
(2)order by语句必须包含limit输出限制 (3)限制执行笛卡尔积的查询
抓取策略
默认所有SQL都回转成MapReduce-----more
对某些情况的查询不需要使用MapReduce计算 set hive.fetch.task.conversion=none/more;
排序
1、Order By - 对于查询结果做全排序,只允许有一个reduce处理 (当数据量较大时,应慎用。严格模式下,必须结合limit来使用) 2、Sort By - 对于单个reduce的数据进行排序 3、Distribute By - 分区排序,经常和Sort By结合使用 ; 可以指定升序还是降序 4、Cluster By - 相当于 Sort By + Distribute By 同一个字段 (Cluster By不能通过asc、desc的方式指定排序规则; 可通过 distribute by column sort by column asc|desc 的方式)
JOIN
1、Map Join:在Map端完成Join 两种实现方式: - SQL方式:在SQL语句中添加MapJoin标记(mapjoin hint),小表会先被加载到内存中 语法:
select /*+MAPJOIN(smallTable)*/ smallTable.key, bigTable.value
FROM smallTable JOIN bigTable ON smallTable.key = bigTable.Key; - 开启自动地MapJoin
set hive.auto.convert.join=true; 会优先对左边的小表进行加载
hive.mapjoin.amslltable.filesize; 大小表阈值判断 (25000000 ≈·25MB )
hive.ignore.mapjoin.hint 默认true(当同时开启自动mapjoin、声明mapjoin);采取哪一种。 true 采取自动
2、Hive join :尽可能使用相同的连接键(会转化为一个MapReduce作业)
3、大表join大表
- 空key过滤:有时join超时是因为某些key对应的数据太多,而相同的key对应的数据都回发送到相同的reducer上,从而导致内存不够。此时我们应该分析这些异常 的key,很多情况下,这些key对应的数据是异常数据,我们需要在SQL语句中进行过滤
- 空key转换:有时虽然某个key为空对应的数据很多,但是相应的数据不是异常数据,必须要包含在join的结果中,此时我们可以再表a中key为空的字段赋一个随机的 值,使得数据随机均匀地分布到不同的reducer上
Map-Side聚合
set hive.map.agger=true; 开启聚合
目的: 综合考虑与reduce端shuffle 的效率
hive.groupby.mapagger.checkinterval; map端group by 执行局和时处理的多少行数据,默认100000 hive.map.aggr.hash.min.reduction; 进行聚合最小比例 (预先对100000条数据做聚合,若聚合后的数据量/100000大于该配置的0.5,则不会聚合) hive.map.aggr.hash.percentmemory; 聚合使用的内存最大值 hive.groupby.skewindata 是否对groupby产生的数据倾斜做优化,默认为false
合并小文件
- 文件小,容易在文件存储端造成压力,给hdfs造成压力,影响效率 - 设置合并属性 是否合并map端输出文件 hive.merge.mapfiles =true 是否合并reduce端输出文件 hive.merge.mapredfiles =true 合并文件的大小 hive.merge.size.per.task=256*1000*1000
去重统计
数据量小的时候,无所谓
数据量大时,由于COUNT DISTINCT 操作需要用一个Reduce Task来完成,这是一个Reduce需要处理的数据量太大,就会导致整个Job很难完成,一般count distinct 会使用先group by 再 Count的方式替换
Map和Reduce的数量
map:
mapred.max.split.size 每个map处理文件的最大值
mapred.min.split.size.per.node 节点上split的最小值
mapred.min.split.size.per.rack 一个机架上split的最小值
数量由block数量控制
reducer
mapred.reduce.tasks 强制指定reduce的任务数量
hive.exec.reducers.bytes.per.reducer 每个任务处理的数据量
hive.exec.reducers.max 每个任务最大的reduce数
JVM重用
使用场景(连接池): - 小文件个数过多 - task个数过多 set mapred.job.reuse.jvm.num.tasks=n 来设置 缺点: 设置开启后,task插槽会一直占用资源,不论是否有task运行,知道所有的task即 整个job全部执行完毕时,才会释放所有的task插槽资源
压缩
配置
支持 的压缩编码 | 压缩格式 | 工具 | 算法 | 文件扩展名 | 是否可切分 | | -------- | ----- | ------- | ---------- | ---------- | | DEFAULT | 无 | DEFAULT | .deflate | 否 | | Gzip | gzip | DEFAULT | .gz | 否 | | bzip2 | bzip2 | bzip2 | .bz2 | 是 | | LZO | lzop | LZO | .lzo | 否 | | LZ4 | 无 | LZ4 | .lz4 | 否 | | Snappy | 无 | Snappy | .snappy | 否 | 为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器,如下表所示 | 压缩格式 | 对应的编码/解码器 | | -------- | ------------------------------------------ | | DEFLATE | org.apache.hadoop.io.compress.DefaultCodec | | gzip | org.apache.hadoop.io.compress.GzipCodec | | bzip2 | org.apache.hadoop.io.compress.BZip2Codec | | LZO | com.hadoop.compression.lzo.LzopCodec | | LZ4 | org.apache.hadoop.io.compress.Lz4Codec | | Snappy | org.apache.hadoop.io.compress.SnappyCodec |
压缩配置参数
要在Hadoop中启用压缩,可以配置如下参数(mapred-site.xml文件中): | 参数 | 默认值 | 阶段 | 建议 | | ------------------------------------------------- | ------------------------------------------------------------ | ----------- | -------------------------------------------- | | io.compression.codecs (在core-site.xml中配置) | org.apache.hadoop.io.compress.DefaultCodec, org.apache.hadoop.io.compress.GzipCodec, org.apache.hadoop.io.compress.BZip2Codec,org.apache.hadoop.io.compress.Lz4Codec | 输入压缩 | Hadoop使用文件扩展名判断是否支持某种编解码器 | | mapreduce.map.output.compress | false | mapper输出 | 这个参数设为true启用压缩 | | mapreduce.map.output.compress.codec | org.apache.hadoop.io.compress.DefaultCodec | mapper输出 | 使用LZO、LZ4或snappy编解码器在此阶段压缩数据 | | mapreduce.output.fileoutputformat.compress | false | reducer输出 | 这个参数设为true启用压缩 | | mapreduce.output.fileoutputformat.compress.codec | org.apache.hadoop.io.compress. DefaultCodec | reducer输出 | 使用标准工具或者编解码器,如gzip和bzip2 | | mapreduce.output.fileoutputformat.compress.type | RECORD | reducer输出 | SequenceFile输出使用的压缩类型:NONE和BLOCK |
开启Map输出压缩
目的:减少map和reduce之间数据传输量 --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)>set mapreduce.map.output.compress.codec= org.apache.hadoop.io.compress.SnappyCodec; --4)执行查询语句 hive (default)> select count(*) from aaaa;
开启Reduce输出阶段压缩
当Hive将输出写入到表中时,输出内容同样可以进行压缩。 --1)开启hive最终输出数据压缩功能 hive (default)>set hive.exec.compress.output=true; --2)开启mapreduce最终输出数据压缩 hive (default)>set mapreduce.output.fileoutputformat.compress=true; --3)设置mapreduce最终数据输出压缩方式 hive (default)> set mapreduce.output.fileoutputformat.compress.codec = org.apache.hadoop.io.compress.SnappyCodec; --4)设置mapreduce最终数据输出压缩为块压缩 hive (default)> set mapreduce.output.fileoutputformat.compress.type=BLOCK; --5)测试一下输出结果是否是压缩文件 hive (default)> insert overwrite local directory '/root/data' select * from aaaa;
文件存储格式
Hive支持的存储数的格式主要有:TEXTFILE 、SEQUENCEFILE、ORC、PARQUET。
TEXTFILE和SEQUENCEFILE的存储格式都是基于行存储的;
ORC和PARQUET是基于列式存储的。 (历史数据,不适用于插入数据); 省空间,但查询有所影响
列式存储可以针对性的设计更好的设计压缩算法
时间
text:0.156
orc :0.127
par :0.17
压缩比: ORC>Parquet>text
- TEXTFILE
默认格式,数据不压缩,磁盘开销大,数据解析开销大,可结合Gzip,Bzip2使用(系统自动检查,执行查询时自动解压
hive不会对数据进行切分,从而无法对数据进行并行操作。
- Orc

默认采用ZLIB压缩
可以看到每个Orc文件由1个或多个stripe组成,每个stripe 250MB大小,这个Stripe相当于RowGroup,不过大小由4MB->250MB
这样应该能提升顺序读的吞吐率。 每个stripe里有三部分组成,分别是
- Index 轻量级索引,每隔1W行做一个索引,只记录每行的个字段在Row Data中的offset
- Row 存的是具体的数据,先取部分行,然后对这些行按列进行存储。对每个列进行了编码,分成多个Stream来存储
- Stripe 存储的是各个Stream的类型,长度等信息
- File Footer 存的是每个Stripe的行数,每个Column的数据类型信息等;每个文件的尾部是一个PostScript,记录了
整个文件的压缩类型以及FileFooter的长度信息等。在读取文件时,会seek到文件的尾部,从中解析File
Footer长度,在读取,从里面解析到各个stripe信息,在读各个stripe,即从后往前读
- PARQUET

Parquet是面向分析型业务的列式存储格式
Parquet文件是以二进制方式存储的,所以是不可以直接读取的,文件中包括该文件的数据和元数据,因此Parquet格式文件是自解析的。
通常情况下,在存储Parquet数据的时候会按照Block大小设置行组的大小,由于一般情况下每一个Mapper任务处理数据的最小单位是一个Block,
这样可以把每一个行组由一个Mapper任务处理,增大任务执行并行度。Parquet文件的格式如下图所示。
ORC存储方式的压缩
官网:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+ORC
CREATE EXTERNAL TABLE psn ( id int, name string, likes ARRAY<string>, address ARRAY<string> ) PARTITIONED BY (gender string) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' COLLECTION ITEMS TERMINATED BY '-' MAP KEYS TERMINATED BY ':' stored as orc LOCATION '/data' tblproperties ("orc.compress"="NONE");
| Key | Default | Notes | | ------------------------ | ---------- | ------------------------------------------------------------ | | orc.compress | ZLIB | high level compression (one of NONE, ZLIB, SNAPPY) | | orc.compress.size | 262,144 | number of bytes in each compression chunk | | orc.stripe.size | 67,108,864 | number of bytes in each stripe | | orc.row.index.stride | 10,000 | number of rows between index entries (must be >= 1000) | | orc.create.index | true | whether to create row indexes | | orc.bloom.filter.columns | "" | comma separated list of column names for which bloom filter should be created | | orc.bloom.filter.fpp | 0.05 | false positive probability for bloom filter (must >0.0 and <1.0) |
HIVE高可用