1.fetch抓取
1)什么是fetch抓取
fetch抓取是指hive对select 所有字段、select 指定字段、limit可以不必使用MapReduce计算,在这种情况下,hive可以简单地读取表对应的存储目录下的文件.
然后输出查询结果到控制台,这比走MR来说,大大减少了运行时间.
2)相关配置
hive.default.xml文件中hive.fetch.task.conversion默认是more.该属性设置为more以后,在全局查找,字段查找,limit查找等都不走mapreduce.
<property>
<name>hive.fetch.task.conversion</name>
<value>more</value>
<description>
值可以选择:[none,minimal,more]
0. none:全部走MR
1. minimal:部分走MR
2. more:全局查找,字段查找,limit查找都不走MR
</description>
</property>
2.本地模式
1)什么是本地模式
大多数MR job是需要并行执行的,有时hive输入的数据量是非常小的,在这种情况下为触发任 务消耗的时间可能会比实际job的执行时间要多的多。此时执行hive本地模式,即在单台计算机上处理所有的任务。对于小的数据集,执行时间可能明显缩短。
2)相关配置
设置hive.exec.mode.local.auto的值为true,来让hive在适当的时候自动启动本地MR模式
set hive.exec.mode.local.auto=true;
设置本地MR的最大数据量,当输入数据量小于这个值时采用local MR模式,默认为128M
set hive.exec.mode.local.auto.inputbytes.max=50000000;
设置local MR的最大输入文件个数,当输入文件个数小于这个值时采用local MR模式,默认为4.
set hive.exec.mode.local.auto.input.files.max=10;
三表的优化
1.小表join大表
实际测试发现,新版的hive小表放在左边和右边已经没有明显区别。当执行map join时,小表join大表和大表join小表没有区别,会加载小表到大表的每个map端,在map端完成join.
以前是:前面的表都会被加载到内存中,后面的表进行磁盘扫描。
2.大表join大表
1)空key过滤
在mapreduce程序中,相同key对应的数据都会发送到相同的reduce task上。而所有值为Null的key都会被发送到同一个reduce task上,如果这样的数据太多,会导致内存不够。很多情况下,这些值为null的key对应的数据也是异常数据,我们可以直接把他们过滤掉。
2)空key转换
有时,空key对应的数据不是异常数据,其数据必须要包括在join中,为了不让所有空key都对应的数据跑到相同的reduce task。出现数据倾斜,内存溢出等问题。我们可以给值为null的赋一个随机的值,使得其对应的数据可以随机且均匀分布在不同的reduce上。
下面这个sql表是t1中的id为null时不参与聚合,在join时,将它的key临时转为一个随机数,这样避免了分到一个reduce中.但是这个t1的id实际上没有个改变,只是join的时候用作key时变了.
select t1.*
from t1 full join t2
on case when t1.id is null then concat('hive',rand()) else t1.id end = t2.id;
3)SMB Join(重点)
SMB=sort merge bucket join
如何进行SMB join?
1.两表是分别以join on的条件建立的分桶表,且分的桶数相同.
2.set hive.optimize.bucketmapjoin=true;
set hive.optimize.bucketmapjoin.sortedmerge=true;
set hive.input.format=org.hadoop.hive.ql.io.BucketizedHiveInputFormat;
3.map join
1)common join
什么是common join?
如果不指定map join或者不符合map join的条件,那么hive解析器会将join操作转换成common join,即:在reduce阶段完成join. 整个过程包含:map、shuffle、reduce阶段。
①map阶段
读取源表的数据,在map端输出时,以join on条件作为key,如果join有多个关联键,则以这些关联键的组合作为key。map输出的value为join之后所关心的(select 或者 where中需要用到的)列。同时,在value中还包含表的tag信息,用于标明此value对应哪个表。
②shuffle阶段
将key-value按照key的hash值推送至不同的reduce中,这样确保两个表中相同的key位于同一个reduce中。
③reduce阶段
根据key的值完成join操作,期间通过tag来识别不同表的数据。
2)hive map join
①什么是map join?
就是在map阶段进行表之间的连接,而不是进入到reduce阶段才进行连接。这样就节省了在shuffle阶段时要进行的大量数据传输,从而起到了优化job的作用。
②map join的原理
通常情况下,相同key的key-value存在于不同的map task中。这样就必须在reduce task阶段连接。要使map task能够顺利进行,那么就必须满足这样的条件:只有一份表的数据分布在不同的map中,其他连接表的数据必须在该表的不同map中有完整的拷贝。
③map join使用的场景
在两个要连接的表中,有一个很大,有一个很小,这样这个小表可以存放在内存中而不影响性能。我们把小表的文件复制到那张大表的map task中,再让map把文件读到内存中待用。
④hive内置提供的优化机制之一就包括map join
在很久以前,这个选项就自动开启:默认小表25M
set hive.auto.convert.join=true;
hive还提供了另一个参数:表文件的大小作为开启和关闭map join的阈值.
hive.mapjoin.smalltable.filesize=25000000 即25M
当指定map join时,小表join大表和大表join小表没有区别。会加载小表到每个map端,在map端完成join。
4.join数据倾斜
set hive.skewjoin.key=100000; 默认为10万.
join的key对应的记录条数超过这个值则会进行拆分,值根据数据量设置
ste hive.optimize.skewjoin=false;
如果是join过程中出现数据清切应该设置为true;
如果开启了,在join的过程中会开启两个MR,通过下面的参数可以控制第二个job的mapper数量
set hive.skewjoin.mapjoin.map.tasks=10000;默认1万;
5.group by
①并不是所有的聚合操作都需要在reduce端完成,很多聚合操作可以先在map端进行预聚合,然后在reduce端得出最终结果。
hive.map.aggr=true;
是否在map端进行预聚合,默认为true
set hive.groupby.mapaggr.checkinterval=100000;
在map端进行聚合操作的条目数目
②hive.groupby.skewindata=true 注:skew倾斜
该方法在reduce端预聚合,聚合程度比map阶段预聚合程度高(因为map片多,个数多).
当选项设定为true,会有两个MR job。
在第一个MR job中,map的输出结果会随机分发到reduce中。每个reduce做部分聚合操作,
并输出结果。这样处理的结果是相同的group by key有可能被分发到不同的reduce中,从而
达到负载均衡的目的。
第二个MR job再根据预处理的数据结果按照group by key分布到一个reduce中(这个可以
保证相同的group by key被分到同一个reduce中)。最后完成最终的聚合操作。
6.count(distinct)去重
该操作在一个reduce task中执行,当数据量大的时候,这个reduce task需要处理的数据量太大,会导致整个job难以完成。用先group by id再count(id) 来替换。
7.笛卡尔积
应尽量避免笛卡尔积。因为只用一个reduce task执行笛卡尔积,出现笛卡尔积的情景:
1)select t1,t2
2)join时不加条件
8.行列过滤
列处理:在select中指定想要的列,尽量避免用select *
行处理:当使用left join、right join、full join时,如果将后面表的过的过滤写在where后面。就会先全表关联,之后再过滤。
四.并行执行
什么是并行执行?
hive会将一个查询转化成一个或多个阶段,这样的阶段可以是mapreduce阶段,limit阶段,或者hive执行过程中可能经历的其他阶段。
默认情况下,hive一次只会执行一个阶段,不过,某个特定的job可能包含众多的阶段,而这些阶段是可以并行执行的,这样可能使整个job执行时间缩短。
通过设置hive.exec.parallel(并行)=true就可以开启并行执行,不过在共享集群中,需要注意,如果Job中并行阶段增多,那么集群利用率就会增加。
set hive.exec.parallel=true;
打开任务并行执行
set hive.exec.parallel.thread.number=16;
同一个sql允许最大并行度,默认为8
当然,得是在系统资源比较空闲的时候才有优势,否则没资源,并行也起不来
五.严格模式
严格模式可以防止用户执行那些可能造成不好影响的查询。开启严格模式:
set hive.mapred.mode=strict;
默认是nonstrict
严格模式限制3种类型的查询
1.对分区表的查询必须在where子句对分区字段限定条件,即不允许扫描所有分区。
2.使用order by 必须学limit
因为order by为了执行全局排序将所有的结果数据分发到一个reduce task中进行处理。强制要求用户增加这个limit语句可以防止reduce task额外执行很长时间。
3.限制笛卡尔积
六.推测执行
在分布式集群环境下,因为程序bug(或hadoop本身的bug),资源分布不均等问题,会造成同一个job的多个任务速度不一致的情况。有些任务的运行速度可能明显慢于其他任务(比如一个job的某个任务的进度只有50%,而其他已经执行完毕),则这个任务会拖累job整体的执行进度。为了避免这种情况发生,hadoop采用了推测执行机制,它根据一定的法则推测出“拖后腿”的任务,让该任务与原始任务同时处理同一份数据,并最终选用最先成功运行完成任务的计算结果作为最终结果。speculative:推测的
设置开启推测执行参数:Hadoop的mapred-site.xml文件中进行配置
<property>
<name>mapreduce.map.speculative</name>
<value>true</value>
</property>
<property>
<name>mapreduce.reduce.speculative</name>
<value>true</value>
</property>
hive本身也提供了配置项来控制reduce-site的推测执行
<property>
<name>hive.mapred.reduce.tasks.speculative.execution</name>
<value>true</value>
</property>
七.HIve Map优化
1.设置合理的Map个数
通常情况下,job会通过input目录产生一个或多个map task,map task的个数由:
input文件总个数,input文件大小,块的大小决定,有多少片,就有多少个maptask。
1)复杂文件增加Map数
当input的文件都很大,任务逻辑复杂,map执行非常慢的时候,可以考虑增加Map数。来使得每个map处理的数据量减少,从而提高任务的执行效率。
增加map的方法:根据下面的公式调节片的大小
computeSlite(Math.max(
minSize,
Math.min(maxSize,blocksize)))=blocksize=128M
调整maxSize最大值,让maxSize最大值低于blocksize就可以增加map的个数.
2)是不是map数越多越好
不是,如果有很多小文件。一个map task处理一个小文件,则每个map task的任务启动和初始化时间远远大于逻辑处理时间,这样会造成大量的资源浪费,而且,同时执行的map task的数量是受限的。
3)是不是每个map处理的接近128M的文件就高枕无忧了?
不一定,比如一个127M的文件,正常会用一个map task去执行,但这个文件中有1个或2个字段,但却有几千万个记录。如果该map处理的逻辑比较复杂,用一个 map任务去做,肯定比较耗时。
3.小文件进行合并(亲测)
1.通过设置读取片的大小来设置
set mapred.max.split.size=1682000000;
切片的最大值
set mapred.min.split.size=1346000000;
切片的最小值
set mapred.min.split.size.per.node=1346000000;
每个节点切片的最小值
set mapred.min.split.size.per.rack=1346000000;
每个机架切片的最小值
2.通过另起一个job来合并小文件
set hive.merge.mapredfiles=true;
对mr进行设置
set hive.merge.mapfiles=true;
only-map进行设置
set hive.merge.smallfiles.avgsize=16000000;默认为16M
set hive.merge.size.per.task=256000000;
合并后每个文件的目标大小,默认为256000000,256M
切分的过程:
1.三个重要的属性:
mapSplitSize:切片大小的最大值
minSplitSizeNode:同一个节点的数据块形成切片时,切片大小的最小值
minSplitSizeRack:通一机架的数据块形成切片时,切片大小的最小值
2.切片形成该过程
1)不断得带节点列表,逐个节点(以块为单位形成切片)
遍历累计每个节点上的数据块,如果累加数据块的大小>=maxSplitSize则将这些数据块形成一 个切片.继续改过程,直到剩余数据块累加大小<maxSplitSize,则进行下一步
2)如果剩余数据块累加大小>=minSizeNode,则将这些数据块形成一个切片。继续该过程,直到数据块累加大小<minSplitSizeNode。然后进行下一步。
3)不断迭代机架列表,逐个机架(以块为单位),形成切片。如果累加数据块大小>=maxSplitSize,则将这些数据块形成一个切片,直到剩余数据块累加大小<maxSplitSize则进行下一步
4)遍历并累加所有机架上的剩余数据块,如果累加数据块大小>=maxSplitSize则将这些数据块形成一个切片,继续该过程,直到剩余数据块累加大小<maxSplitSize,则进行下一步。
5)最终将剩余的数据块形成一个切片。
八.设置合理的reduce task数
1.调整reduce个数方法一
1)默认每个Reduce处理的数据量默认是256M
set hive.exec.reducers.bytes.per.reducer=256000000;
2)每个application最大的reduce数,默认为1009,如果指定为负值,则hive在自动
确定reducer数量时将此值作为最大的reducer数量.
set hive.exec.reducers.max=1009
3)计算reducer数的公式
N=min(参数2,总输入数据量/参数1)
2.调整reduce个数方法二
在hadoop的maped-default.xml文件中修改
设置每个Job的Reduce个数
set mapreduce.job.reduces=15;默认为-1表示禁用
3.reduce task个数并不是越多越好
过多的启动和初始化reduce task会消耗时间和资源.另外有多少个reduce task就会有多少个输出文件,如果生成了很多小文件,那么如果这些小文件作为下一个任务输入,则会出现小文件过多的问题。
在设置reduce个数的时候也需要考虑这两个原则:处理大数据量利用合适的reduce数,使单个reduce任务处理数据量大小要合适。
九.数据倾斜
group by数据倾斜:map aggre、skewindata两个job
多个key同时导致数据倾斜:多设置reduce task个数
join数据倾斜:map join、skewjoin两个job、SMB、空值处理
十.explain查看执行计划
用法:
explain extended select xxx from xxx;
十一.谓词下推
将SQL语句的where谓词逻辑都尽可能提前执行,减少下游处理的数据量。对应逻辑优化器是:PredicatePushDown.
set hive.optimize.ppd.=true;谓词下推,默认是true
会自动将join后where中的where提前:先where再join
textfile 和sequencefile的存储格式都是基于行存储的。
十二.合适的文件格式
1.列式存储和行式存储
1)行存储的特点
当查询满足条件的一整行数据时,列存储需要去每个聚集的字段找到对应的每个列的值,行存储只需要找到其中的第一个值,其余的值都在相邻地方,所以此时行存储查询的速度更快。
2)列存储的特点
因为每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量。表中相同列的值的类型肯定是相同的,列式存储可以针对性的设计更好的压缩算法。
orc和parquet是基于列式存储的。
2.textfile
默认格式,数据不做压缩,磁盘开销大,有序列化和反序列化的开销。压缩后的textfile无法分割和合并,查询效率最低(序列化和反序列化),可以直接存储,load的速度最快。
3.orc格式
建表语句
create table xx(
...
)
row format delimited
fields terminated by '\t'
stored as orc tblproperties("orc.compress"="NONE");
snappy压缩:
stored as orc tblproperties("orc.compress"="SNAPPY");
也可以通过alter table来设置
alter table xx set tblproperties('orc.bloom.filter.columns'='*','orc.compress'='SNAPPY');
orc是列式存储的。使用orc格式,可以降低磁盘存储空间,查询效率也不会差,建议使用orc的snappy压缩。
由于数据压缩,数据量变小,所以Job启动后生成的map,reduce数目也会变少,占用的container就会少,所以变相增加了集群的计算能力。
4.parquet
spark默认为parquet,具有很好的压缩性能,可以指定每一列的压缩方式, 可以减少大量的表扫描、序列化和反序列化的时间。支持复杂的嵌套数据烈性:json。
建表语句
create table xxx(
xxx
)
row format delimited
fields terminated by "\t"
stored as parquet
;
--设置压缩格式为snappy,根据需要来设置压缩格式,默认不压缩
alter table xxx tblproperties('parquet.compression'='SNAPPY');
5.总结
1.textfile存储空间消耗比较大,并且压缩的text无法分割和合并。查询效率最低,因为要反序列化。可以直接存储, load数据的速度最高。
2.orc,查询效率高(二进制存储,不需要反序列化,是自解析的,且为列式存储)。load时需要将text转换成parquet,速度低。因为存储空间少,所以也减少了maptask reducetask,和container的数量,提高集群的计算能力。无论在存储还是查询上,orc都比parquet好。
3.parquet,查询效率高(二进制存储,不需要反序列化,是自解析的,且为列式存储)。load时需要将text转换成parquet,速度低。因为存储空间少,所以也减少了maptask reducetask,和container的数量,提高集群的计算能力。适配spark,impala。