大数据调优总结-干货

mr调优、

 

最简单的调优方式

设置Combiner

Combiner在Map端提前进行了一次Reduce处理。 

可减少Map Task中间输出的结果,从而减少各个Reduce Task的远程拷贝数据量,最终表现为Map Task和Reduce Task执行时间缩短。

选择合理的Writable类型

为应用程序处理的数据选择合适的Writable类型可大大提升性能。 

比如处理整数类型数据时,直接采用IntWritable比先以Text类型读入在转换为整数类型要高效。 

如果输出整数的大部分可用一个或两个字节保存,那么直接采用VIntWritable或者VLongWritable,它们采用了变长整型的编码方式,可以大大减少输出数据量。

作业级别调优

增加输入文件的副本数

假设集群有1个Namenode+8个Datanode节点,HDFS默认的副本数为3 

那么map端读取数据的时候,在启动map task的机器上读取本地的数据为3/8,一部分数据是通过网络从其他节点拿到的 

那么如果副本数设置为8会是什么情况? 

相当于每个子节点上都会有一份完整的数据,map读取的时候直接从本地拿,不需要通过网络这一层了

但是在实际情况中设置副本数为8是不可行的,因为数据本身非常庞大,副本数超过5对集群的磁盘就非常有压力了,所以这项设置需要酌情处理

该配置在hdfs-side.xml的dfs.replication项中设置

 

1、常用combiner来减少输入到reduce的数据,防止数据倾斜

2、在数据通过远程到达reduce端进行处理的时候,可以合理的设置归并排序的次数,过多过少都不好,过多可能会写入磁盘,过少可能会增加归并排序数量

3、设置reduce的个数,来均衡原来输入到一个reduce上的数据,防止数据倾斜

 

spark调优,运行原理

1. RDD的持久化 

cahce() 

persist() 

checkpoint() 

2. 避免创建重复的RDD 

3.尽可能复用同一个RDD 

类似于多个RDD的数据有重叠或者包含的情况,应该尽量复用一个RDD,以尽可能减少RDD的数量,从而减少算子计算次数 

4.尽量避免使用shuffle类算子 

spark运行过程中,最消耗性能的地方就是shuffle过程(简单说,就是将分布在集群中多个节点上的同一个key拉取到同一个节点上进行操作) 

shuffle过程中,各个节点上相同的key都会先写入本地磁盘文件中,然后其他节点需要通过网络传输拉取各个节点上的磁盘文件中的相同的key,而且相同key都拉取到同一个节点进行聚合操作时,还可能会因为一个节点上处理的key过多,导致内存不够存放,从而溢写到磁盘文件中。 

磁盘IO和网络数据传输也是shuffle性能较差的主要原因。 

尽量使用map类的非shuffle算子。 

repartition(重分区)类操作:repartition、repartitionAndSortWithinPartitions、coalesce等 

bykey类操作:reduceByKey、groupByKey、sortByKey等 

join类操作:join、cogroup等 

类如:join –>Broadcast+map (Broadcast:数据量小于1G内的RDD,每份数据放入executor副本中) 

5.使用map-side预聚合的shuffle操作 

因为业务需要,一定要使用shuffle操作,无法用map类算子替代,尽量使用map-side预聚合的算子。(在每个节点本地对相同的key进行一次聚合操作) 

map-side预聚合之后,每个节点本地就只会有一条相同的key,因为多条相同的key都被聚合起来了。其他节点在拉取所有节点上的相同key时,就会大大减少需要拉取的数据数量,从而也就减少了磁盘IO以及网络传输开销。 

通常来说,在可能的情况下,建议使用reduceByKey或者aggregateByKey算子来替代掉groupByKey算子。因为reduceByKey和aggregateByKey算子都会使用用户自定义的函数对每个节点本地的相同key进行预聚合。而groupByKey算子是不会进行预聚合的,全量的数据会在集群的各个节点之间分发和传输,性能相对来说比较差。 

 

groupByKey中 shuffle操作中没有预聚合操作 

 

6.使用高性能的算子 

除了shuffle相关的算子有优化原则外,其他的算子也有相应的优化原则。 

a . 使用mapPartitions替代map。一次函数调用会处理一个partition所有的数据,而不是一次函数调用处理一条。但是可能会出现内存溢出(OOM)问题。 

b . 使用foreachPartitions提到foreach。 

c . 使用filter之后进行coalesce操作。使用filter后,建议使用coalesce算子,手动减少RDD的数据量,将RDD的数据压缩到更少的partition中去。 (coalesce:RDD的分区进行重新划分,repartition只是coalesce接口中shuffle为true的简易实现) 

d . 使用repartitionAndSortWithinPartitions替代repartition与sort类操作。如果需要在repartition重分区后,还有进行排序,建议使用repartitionAndSortWithinPartitions。因为该算子可以一边进行重分区的shuffle操作,一边进行排序(同时进行)。 

7.广播大变量 

在开发中,遇到需要在算子函数中使用外部变量的场景(如配置文件)(尤其是大变量,比如100M以上的大集合)。此时就应该使用Spark的广播功能来提升性能 

在算子函数中使用到外部变量时,默认情况下,spark会将变量复制多个副本,通过网络传递到task中,此时每个task都有一个变量副本。若使用的外部变量比较大,建议使用Spark的广播功能,对该变量进行广播。广播后的变量会保证每个executor的内存中,只驻留一份变量副本,而executor中的task执行时共享该executor中的那份变量副本。从而大大减少变量副本的数量,减少网络传输的性能开销,并减少对executor内存的占用开销。

8.使用kryo优化序列化性能 

JAVA序列化:是指把JAVA对象转换位字节序列的过程;而JAVA反序列化是指把字节序列恢复为JAVA对象的过程。 

当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等,而这些数据都会以二进制序列的形式在网络上传送。那么当两个JAVA进程进行通信时,要实现进程间对象传送就需要JAVA序列化和反序列化。 也就是:发送方需要把这个JAVA对象转换为字节序列,然后再网络上传送;另一方面,接收方需要从字节序列中回复出JAVA对象。 

JAVA序列化的好处有:实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上;利用序列化实现远程通信,即在网络上传送对象的字节序列。

spark中主要有三个地方涉及到了序列化: 

1.在算子函数中使用到外部变量时,该变量会被序列化后进行网络传输 

2.将自定义的类型作为RDD的泛型类型(如JavaRDD,student是自定义类型),所有自定义类型对象都会进行序列化,这种情况下,也要求自定义的类必须实现Serializable接口 

3. 使用可序列化的持久化策略时(如MEMORY_ONLY_SER),spark会将RDD中每个partition都序列化成一个大的字节数组

对于这三种出现序列化的地方,我们都可以通过使用Kryo序列化类库,来优化序列化和反序列化。Spark默认使用的是Java的序列化机制,也就是ObjectOutputStream/ObjectInputStream API来进行序列化和反序列化。但是spark同时支持使用Keyo序列化库,Kryo序列化类库的性能比Java序列化类库的性能高很多。

SparkConf().set(“spark.serializer”, “org.apache.spark.serializer.KryoSerializer”)

Scala版本: 

val conf = new SparkConf().setMaster(…).setAppName(…) 

conf.registerKryoClasses(Array(classOf[Counter] )) 

val sc = new SparkContext(conf)

Java版本: 

SparkConf conf = new SparkConf().setMaster(…).setAppName(…) 

conf.registerKryoClasses(Counter.class) 

JavaSparkContext sc = new JavaSparkContext(conf)

如果注册的要序列化的自定义的类型,本身特别大,比如包含了超过100个field。那么就会导致要序列化的对象过大。此时就需要对Kryo本身进行优化。因为Kryo内部的缓存可能不够存放那么大的class对象。此时就需要调用SparkConf.set()方法,设置spark.kryoserializer.buffer.mb参数的值,将其调大。

默认情况下它的值是2,就是说最大能缓存2M的对象,然后进行序列化。可以在必要时将其调大。比如设置为10。

9.优化数据结构 

Java中,有三种类型比较耗费内存: 

1.对象:每个JAVA对象都有对象头、引用等额外的信息。比较占用内存空间 

2.字符串:每个字符串内部都有一个字符数组以及长度等额外信息 

3.集合类型:比如HashMap、LinkedList等,因为集合类型内部通常会使用一些内部类来封装集合元素,比如Map.Entry

因此,在Spark编码实现中,特别是对于算子函数中的代码,尽量不要使用上述的三种数据结构,尽量使用字符串替代对象,使用原始类型(Int、Long)替代字符串使用数组替代集合类型。可以尽可能减少内存占用。

 

 

9. hive优化有哪些?

 

理解hadoop的核心能力,是hive优化的根本。这是这一年来,项目组所有成员宝贵的经验总结。

长期观察hadoop处理数据的过程,有几个显著的特征:

1.不怕数据多,就怕数据倾斜。

2.对jobs数比较多的作业运行效率相对比较低,比如即使有几百行的表,如果多次关联多次汇总,产生十几个jobs,没半小时是跑不完的。map reduce作业初始化的时间是比较长的。

3.对sum,count来说,不存在数据倾斜问题。

4.对count(distinct ),效率较低,数据量一多,准出问题,如果是多count(distinct )效率更低。

优化可以从几个方面着手:

1. 好的模型设计事半功倍。

2. 解决数据倾斜问题。

3. 减少job数。

4. 设置合理的map reduce的task数,能有效提升性能。(比如,10w+级别的计算,用160个reduce,那是相当的浪费,1个足够)。

5. 自己动手写sql解决数据倾斜问题是个不错的选择。set hive.groupby.skewindata=true;这是通用的算法优化,但算法优化总是漠视业务,习惯性提供通用的解决方法。 Etl开发人员更了解业务,更了解数据,所以通过业务逻辑解决倾斜的方法往往更精确,更有效。

6. 对count(distinct)采取漠视的方法,尤其数据大的时候很容易产生倾斜问题,不抱侥幸心理。自己动手,丰衣足食。

7. 对小文件进行合并,是行至有效的提高调度效率的方法,假如我们的作业设置合理的文件数,对云梯的整体调度效率也会产生积极的影响。

8. 优化时把握整体,单个作业最优不如整体最优。

 

整体架构优化点:

 

1、根据不同业务需求进行日期分区,并执行类型动态分区。

相关参数设置:

0.14中默认hive.exec.dynamic.partition=ture

 

2、为了减少磁盘存储空间以及I/O次数,对数据进行压缩

相关参数设置:

job输出文件按照BLOCK以Gzip方式进行压缩。

3、hive中间表以SequenceFile保存,可以节约序列化和反序列化的时间

4、yarn优化,在此不再展开,后面专门介绍。

二、MR阶段优化

reduce切割算法:

 

相关参数设置,默认为:

hive.exec.reducers.max=999

hive.exec.reducers.bytes.per.reducer=1G 

reduce task num=min{reducers.max,input.size/bytes.per.reducer},可以根据实际需求来调整reduce的个数。

 

四、SQL优化

 

整体的优化策略如下:

  •     去除查询中不需要的column
  •     Where条件判断等在TableScan阶段就进行过滤
  •     利用Partition信息,只读取符合条件的Partition
  •     Map端join,以大表作驱动,小表载入所有mapper内存中
  •     调整Join顺序,确保以大表作为驱动表
  •     对于数据分布不均衡的表Group by时,为避免数据集中到少数的reducer上,分成两个map-reduce阶段。第一个阶段先用Distinct列进行shuffle,然后在reduce端部分聚合,减小数据规模,第二个map-reduce阶段再按group-by列聚合。
  •     在map端用hash进行部分聚合,减小reduce端数据处理规模。

Hive中常用的文件格式有哪些?

1.textfile

textfile为默认格式

存储方式:行存储

磁盘开销大 数据解析开销大

压缩的text文件 hive无法进行合并和拆分

2.sequencefile

二进制文件,以<key,value>的形式序列化到文件中

存储方式:行存储

可分割 压缩

一般选择block压缩

优势是文件和hadoop api中的mapfile是相互兼容的。

 

3.rcfile

存储方式:数据按行分块 每块按照列存储

压缩快 快速列存取

读记录尽量涉及到的block最少

读取需要的列只需要读取每个row group 的头部定义。

读取全量数据的操作 性能可能比sequencefile没有明显的优势

 

4.orc

存储方式:数据按行分块 每块按照列存储

压缩快 快速列存取

效率比rcfile高,是rcfile的改良版本

 

5.自定义格式

用户可以通过实现inputformat和 outputformat来自定义输入输出格式。

如何用Spark编写一个Wordcount?

 

 

优化1——数据库优化面试题

1.实践中如何优化mysql

1) SQL语句及索引的优化

2) 数据库表结构的优化

3) 系统配置的优化

4) 硬件优化

 

2.索引的底层实现原理和优化

在 DB2 数据库中索引采用的是 B+ 树的结构,索引的叶子节点上包含索引键的值和一个指向数据地址的指针。DB2 先查询索引,然后通过索引里记录的指针,直接访问表的数据页。

B+树。B+树是应数据库所需而出现的一种B树的变形树。

B+树的特点:

(1)所有叶节点包含全部关键字及指向相应记录的指针,而且叶节点中将关键字按大小顺序排列,并且相邻叶节点按大小顺序相互链接起来。

(2)所有分支节点(可看做索引的索引)中仅包含它的各个子节点(即下一级的索引块)中关键字的最大值即指向其子节点的指针。

(3)B+树中,叶节点包含信息,所有非叶结点仅起到索引作用,非叶节点中的每个索引项只含有对应子树的最大关键字和指向该子树的指针,不含有该关键字对应记录的存储地址。

(4)叶节点包含了所有的关键字,即在非叶节点出现的关键字也会出现在叶子节点中。

B+树有两个头指针,一个指向根节点,另一个指向关键字最小的叶节点。B+树进行两种查找运算:从最小关键字开始的顺序查找,另一种从根节点开始的多路查找。

原理:叶子节点是按关键字大小顺序排列,且增加了指向下一个叶子节点的指针。

优化:InnoDB建议大部分表使用默认的自增的主键作为索引

MsSql、DB2使用的是B+Tree,Oracle及Sysbase使用的是B-Tree

SQL语句的优化

1) 尽量避免耗时操作。

带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL语句会启动SQL引 执行,耗费资源的排序(SORT)功能。DISTINCT需要一次排序操作, 而其他的至少需要执行两次排序

2) 如果无需排除重复值或是操作集无重复则用UNION ALL, UNION更费事(因为要比较)

UNION因为会将各查询子集的记录做比较,故比起UNION ALL ,通常速度都会慢上许多。一般来说,如果使用UNION ALL能满足要求的话, 务必使用UNION ALL。还有一种情况大家可能会忽略掉,就是虽然要求几个子集的并集需要过滤掉重复记录,但由于脚本的特殊性,不可能存在重复记录,这时便应该使用UNION ALL,如xx模块的某个查询程序就曾经存在这种情况,见,由于语句的特殊性,在这个脚本中几个子集的记录绝对不可能重复,故可以改用UNION ALL)连接操作

3) 避免在WHERE子句中使用in,not  in,or 或者having。

可以使用 exist 和not exist代替 in和not in。

可以使用表链接代替 exist。

Having可以用where代替,如果无法代替可以分两步处理。

例子

  1. SELECT * FROM ORDERS WHERE CUSTOMER_NAME NOT IN
  2. (SELECT CUSTOMER_NAME FROM CUSTOMER)

优化

  1. SELECT * FROM ORDERS WHERE CUSTOMER_NAME not exist
  2. (SELECT CUSTOMER_NAME FROM CUSTOMER)

4) 不要在建立的索引的数据列上进行下列操作:

(1)避免对索引字段进行计算操作

(2)避免在索引字段上使用not,<>,!=

(3)避免在索引列上使用IS NULL和IS NOT NULL

(4)避免在索引列上出现数据类型转换

(5)避免在索引字段上使用函数

例如:where trunc(create_date)=trunc(:date1)

虽然已对create_date 字段建了索引,但由于加了TRUNC,使得索引无法用上。此处正确的写法应该是

where create_date>=trunc(:date1) and create_date

(6)避免建立索引的列中使用空值。

5) 查询的模糊匹配

尽量避免在一个复杂查询里面使用 LIKE '%parm1%'—— 红色标识位置的百分号会导致相关列的索引无法使用,最好不要用。

解决办法:

其实只需要对该脚本略做改进,查询速度便会提高近百倍。改进方法如下:

a、修改前台程序——把查询条件的供应商名称一栏由原来的文本输入改为下拉列表,用户模糊输入供应商名称时,直接在前台就帮忙定位到具体的供应商,这样在调用后台程序时,这列就可以直接用等于来关联了。

b、直接修改后台——根据输入条件,先查出符合条件的供应商,并把相关记录保存在一个临时表里头,然后再用临时表去做复杂关联

6) 避免使用临时表

(1)除非却有需要,否则应尽量避免使用临时表,相反,可以使用表变量代替;

(2)大多数时候(99%),表变量驻扎在内存中,因此速度比临时表更快,临时表驻扎在TempDb数据库中,因此临时表上的操作需要跨数据库通信,速度自然慢。

可以使用联合(UNION)来代替手动创建的临时表

MySQL 从 4.0 的版本开始支持 UNION 查询,它可以把需要使用临时表的两条或更多的 SELECT 查询合并的一个查询中。在客户端的查询会话结束的时候,临时表会被自动删除,从而保证数据库整齐、高效。使用 UNION 来创建查询的时候,我们只需要用UNION作为关键字把多个SELECT语句连接起来就可以了,要注意的是所有 SELECT 语句中的字段数目要想同。下面的例子就演示了一个使用 UNION的查询。

代码如下:

  1. SELECT Name, Phone FROM client UNION SELECT Name, BirthDate FROM author
  2. UNION
  3. SELECT Name, Supplier FROM product

7) 尽量少做重复的工作

尽量减少无效工作,但是这一点的侧重点在客户端程序,需要注意的如下:

A、 控制同一语句的多次执行,特别是一些基础数据的多次执行是很多程序员很少注意的

B、减少多次的数据转换,也许需要数据转换是设计的问题,但是减少次数是程序员可以做到的。

C、杜绝不必要的子查询和连接表,子查询在执行计划一般解释成外连接,多余的连接表带来额外的开销。

D、合并对同一表同一条件的多次UPDATE,比如

UPDATE EMPLOYEE SET FNAME='HAIWER' WHERE EMP_ID=' VPA30890F'

UPDATE EMPLOYEE SET LNAME='YANG' WHERE EMP_ID=' VPA30890F'

这两个语句应该合并成以下一个语句

UPDATE EMPLOYEE SET FNAME='HAIWER',LNAME='YANG'

WHERE EMP_ID=' VPA30890F'

E、UPDATE操作不要拆成DELETE操作+INSERT操作的形式,虽然功能相同,但是性能差别是很大的。

F、不要写一些没有意义的查询,比如

SELECT * FROM EMPLOYEE WHERE 1=2

Where后面的原则

第一个原则:在where子句中应把最具限制性的条件放在最前面。

第二个原则:where子句中字段的顺序应和索引中字段顺序一致。

select field3,field4 from tb where upper(field2)='RMN'不使用索引。

如果一个表有两万条记录,建议不使用函数;如果一个表有五万条以上记录,严格禁止使用函数!两万条记录以下没有限制。

 

3.什么情况下设置了索引但无法使用,索引无效

1) 以”%”开头的LIKE语句,模糊匹配:红色标识位置的百分号会导致相关列的索引无法使用

2) Or语句前后没有同时使用索引

3) 数据类型出现隐式转化(如varchar不加单引号的话可能会自动转换为int型,会使索引无效,产生全表扫描。)

4) 在索引列上使用IS NULL 或IS NOT NULL操作。索引是不索引空值的,所以这样的操作不能使用索引,可以用其他的办法处理,例如:数字类型,判断大于0,字符串类型设置一个默认值,判断是否等于默认值即可

5) 在索引字段上使用not,<>,!=,eg<> 操作符(不等于):不等于操作符是永远不会用到索引的,因此对它的处理只会产生全表扫描。 用其它相同功能的操作运算代替,如 a<>0 改为 a>0 or a<0

6) 对索引字段进行计算操作

7) 在索引字段上使用函数

 

4.如何设计一个高并发的系统

1) 数据库的优化,包括合理的事务隔离级别、SQL语句优化、索引优化

2) 使用缓存、尽量减少数据库IO

3) 分布式数据库、分布式缓存

4) 服务器的负载均衡

 

hive调优手段 (1)利用列裁剪 当待查询的表字段较多时,选取需要使用的字段进行查询,避免直接select *出大表的所有字段,以免当使用Beeline查询时控制台输出缓冲区被大数据量撑爆。 (2)JOIN避免笛卡尔积 JOIN场景应严格避免出现笛卡尔积的情况。参与笛卡尔积JOIN的两个表,交叉关联后的数据条数是两个原表记录数之积,对于JOIN后还有聚合的场景而言,会导致reduce端处理的数据量暴增,极大地影响运行效率。 以下左图为笛卡尔积,右图为正常Join。 (3)启动谓词下推 谓词下推(Predicate Pushdown)是一个逻辑优化:尽早的对底层数据进行过滤以减少后续需要处理的数据量。通过以下参数启动谓词下推。 (4)开启Map端聚合功能 在map中会做部分聚集操作,能够使map传送给reduce的数据量大大减少,从而在一定程度上减轻group by带来的数据倾斜。通过以下参数开启map端聚合功能。 (5)使用Hive合并输入格式 设置Hive合并输入格式,使Hive在执行map前进行文件合并,使得本轮map处理数据量均衡。通过以下参数设置Hive合并输入格式。 (6)合并小文件 启动较多的map或reduce能够提高并发度,加快任务运行速度;但同时在HDFS上生成的文件数目也会越来越多,给HDFS的NameNode造成内存上压力,进而影响HDFS读写效率。 对于集群的小文件(主要由Hive启动的MR生成)过多已造成NameNode压力时,建议在Hive启动的MR中启动小文件合并。 小文件合并能够使本轮map输出及整个任务输出的文件完成合并,保证下轮MapReduce任务map处理数据量均衡。 (7)解决group by造成的数据倾斜 通过开启group by倾斜优化开关,解决group by数据倾斜问题。 开启优化开关后group by会启动两个MR。第一个 MR Job 中,Map 的输出结果集合会随机分布到 Reduce 中,每个Reduce做部分聚合操作,并输出结果,这样处理的结果是相同的Group By Key有可能被分发到不同的Reduce中,从而达到负载均衡的目的;第二个MR Job再根据预处理的数据结果按照Group By Key分布到Reduce中(这个过程可以保证相同的Group By Key被分布到同一个Reduce中),最后完成最终的聚合操作。 (8)解决Join造成的数据倾斜 两个表关联键的数据分布倾斜,会形成Skew Join。 解决方案是将这类倾斜的特殊值(记录数超过hive.skewjoin.key参数值)不落入reduce计算,而是先写入HDFS,然后再启动一轮MapJoin专门做这类特殊值的计算,期望能提高计算这部分值的处理速度。设置以下参数。 (9)合理调整map和reduce的内存及虚拟核数 map和reduce的内存及虚拟核数设置,决定了集群资源所能同时启动的container个数,影响集群并行计算的能力。 对于当前任务是CPU密集型任务(如复杂数学计算)的场景:在map和reduce的虚拟核数默认值基础上,逐渐增大虚拟核数进行调试(mapreduce.map.cpu.vcores和mapreduce.reduce.cpu.vcores参数控制),但不要超过可分配给container的虚拟核数(yarn.nodemanager.resource.cpu-vcores参数控制)。 对于当前任务是内存密集型任务(如ORC文件读取/写入、全局排序)的场景:在map和reduce的内存默认值基础上,逐渐增大内存值进行调试(mapreduce.map.memory.mb和mapreduce.reduce.memory.mb参数控制),但不要超过当前NodeManager上可运行的所有容器的物理内存总大小(yarn.nodemanager.resource.memory-mb参数控制)。 (10)合理控制map的数量 map的数量会影响MapReduce扫描、过滤数据的效率。 对于扫描、过滤数据的逻辑比较复杂、输入数据量较大条数较多的场景:根据集群总体资源情况,以及分配给当前租户的资源情况,在不影响其他业务正常运行的条件下,map数量需要适当增大,增加并行处理的力度。 (11)合理控制reduce的数量 reduce数量会影响MapReduce过滤、聚合、对数据排序的效率。 对于关联、聚合、排序时reduce端待处理数据量较大的场景:首先根据每个reduce处理的合适数据量控制reduce的个数,如果每个reduce处理数据仍然很慢,再考虑设置参数增大reduce个数。另一方面,控制能启动的reduce最大个数为分配给当前租户的资源上限,以免影响其他业务的正常运行。 (12)将重复的子查询结果保存到中间表 对于指标计算类型的业务场景,多个指标的HQL语句中可能存在相同的子查询,为避免重复计算浪费计算资源,考虑将重复的子查询的计算结果保存到中间表,实现计算一次、结果共享的优化目标。 (13)启用相关性优化器 相关性优化,旨在利用下面两种查询的相关性: (a)输入相关性:在原始operator树中,同一个输入表被多个MapReduce任务同时使用的场景; (b)作业流程的相关性:两个有依赖关系的MapReduce的任务的shuffle方式相同。 通过以下参数启用相关性优化: 相关参考: https://cwiki.apache.org/confluence/display/Hive/Correlation+Optimizer (14)启用基于代价的优化 基于代价的优化器,可以基于代价(包括FS读写、CPU、IO等)对查询计划进行进一步的优化选择,提升Hive查询的响应速度。 通过以下参数启用基于代价的优化: 相关参考: https://cwiki.apache.org/confluence/display/Hive/Cost-based+optimization+in+Hive (15)启用向量化查询引擎 传统方式中,对数据的处理是以行为单位,依次处理的。Hive也采用了这种方案。这种方案带来的问题是,针对每一行数据,都要进行数据解析,条件判断,方法调用等操作,从而导致了低效的CPU利用。 向量化特性,通过每次处理1024行数据,列方式处理,从而减少了方法调用,降低了CPU消耗,提高了CPU利用率。结合JDK1.8对SIMD的支持,获得了极高的性能提升。 通过以下参数启用向量化查询引擎: 相关参考: https://cwiki.apache.org/confluence/display/Hive/Vectorized+Query+Execution (16)启用Join相关优化 (a)使用MapJoin。MapJoin是针对以下场景进行的优化:两个待连接表中,有一个表非常大,而另一个表非常小,以至于小表可以直接存放到内存中。这样小表复制多份,在每个map task内存中存在一份(比如存放到hash table中),然后只扫描大表。对于大表中的每一条记录key/value,在hash table中查找是否有相同的key的记录,如果有,则连接后输出即可。 (b)使用SMB Join。 相关参考: https://cwiki.apache.org/confluence/display/Hive/LanguageManual+JoinOptimization (17)使用Multiple Insert特性 以下左图为普通insert,右图为Multiple Insert,减少了MR个数,提升了效率。 (18)使用TABLESAMPLE取样查询 在Hive中提供了数据取样(SAMPLING)的功能,用来从Hive表中根据一定的规则进行数据取样,Hive中的数据取样支持数据块取样和分桶表取样。 以下左图为数据块取样,右图为分桶表取样: (19)启用Limit优化 启用limit优化后,使用limit不再是全表查出,而是抽样查询。涉及参数如下: (20)利用局部排序 Hive中使用order by完成全局排序,正常情况下,order by所启动的MR仅有一个reducer,这使得大数据量的表在全局排序时非常低效和耗时。 当全局排序为非必须的场景时,可以使用sort by在每个reducer范围进行内部排序。同时可以使用distribute by控制每行记录分配到哪个reducer。 (21)慎用低性能的UDF和SerDe 慎用低性能的UDF和SerDe,主要指谨慎使用正则表达式类型的UDF和SerDe。如:regexp、regexp_extract、regexp_replace、rlike、RegexSerDe。 当待处理表的条数很多时,如上亿条,采用诸如([^ ]*)([^ ]*)([^]*)(.?)(\".*?\")(-|[0-9]*)(-|[0-9]*)(\".*?\")(\".*?\")这种复杂类型的正则表达式组成过滤条件去匹配记录,会严重地影响map阶段的过滤速度。 建议在充分理解业务需求后,自行编写更高效准确的UDF实现相应的功能。 (22)优化count(distinct) 优化方式如下,左图为原始HQL,右图为优化后HQL。 (23)改用MR实现 在某些场景下,直接编写MR比使用HQL更加高效。

 

 

  • 1
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值