觉得还行的话,点个赞哟。
一 spark RDD分区原则
在Spark的Rdd中,Rdd默认是分区的。
有时候需要重新设置Rdd的分区数量,比如Rdd的分区中,Rdd分区比较多,但是每个Rdd的数据量比较小,则需要重新设置一个比较合理的分区数。或者需要把Rdd的分区数量调大。还有就是通过设置一个Rdd的分区来达到设置生成的文件的数量。
二.coalesce和repartition partitionby(后面一点说)
有两种方法是可以重设Rdd的分区:分别是 coalesce()方法和repartition()。
1.后者是前者的特殊情况,即原有分区如果为4,若 .repartition(8) <=> .coalesce(8,true) 就会将原有分区数改为8 ,一般用于将原有分区数扩大。shuffle=true ,表示必须进行洗牌操作(该操作需要占用一定的时间,shuffle操作可以增加park程序的并行度),才能增大分区。如果coalesce(8) <=>coalesce(8,false) 则该方法不起作用,分区数仍为4
2. 如果需要将原有分区N减小到M个分区,则只能用coalesce,且分两种情况:
如果N>M并且N和M相差不多,(假如N是1000,M是100)那么就可以将N个分区中的若干个分区合并成一个新的分区,最终合并为M个分区,这时可以将shuffle设置为false(以节约运行时间)
如果N>M并且两者相差悬殊,这时如果将shuffle设置为false,父子RDD是窄依赖关系,他们同处在一个Stage中,就可能造成spark程序的并行度不够,从而影响性能,如果在M为1的时候,为了使coalesce之前的操作有更好的并行度,可以将shuffle设置为true。
三. 关于宽依赖和窄依赖
窄依赖是指父Rdd的分区最多只能被一个子Rdd的分区所引用,即一个父Rdd的分区对应一个子Rdd的分区,或者多个父Rdd的分区对应一个子Rdd的分区。
其中map操作 就是窄依赖,窄依赖是懒加载的,不进行shuffle,不会立即执行。
而宽依赖就是宽依赖是指子RDD的分区依赖于父RDD的多个分区或所有分区,即存在一个父RDD的一个分区对应一个子RDD的多个分区。1个父RDD分区对应多个子RDD分区,这其中又分两种情况:1个父RDD对应所有子RDD分区(未经协同划分的Join)或者1个父RDD对应非全部的多个RDD分区(如groupByKey)。 join 需要针对同一个 key 合并,所以需要 shuffle
窄依赖跟宽依赖的区别是是否发生 shuffle(洗牌) 操作.宽依赖会发生 shuffle 操作. 窄依赖是子 RDD的各个分片(partition)不依赖于其他分片,能够独立计算得到结果,宽依赖指子 RDD 的各个分片会依赖于父RDD 的多个分片,所以会造成父 RDD 的各个分片在集群中重新分片, 看如下两个示例:
// Map: "cat" -> c, cat
val rdd1 = rdd.Map(x => (x.charAt(0), x))
// groupby same key and count
val rdd2 = rdd1.groupBy(x => x._1).
Map(x => (x._1, x._2.toList.length))
第一个 Map 操作将 RDD 里的各个元素进行映射, RDD 的各个数据元素之间不存在依赖,可以在集群的各个内存中独立计算,也就是并行化,第二个 groupby 之后的 Map 操作,为了计算相同 key 下的元素个数,需要把相同 key 的元素聚集到同一个 partition 下,所以造成了数据在内存中的重新分布,即 shuffle 操作.shuffle 操作是 spark 中最耗时的操作,应尽量避免不必要的 shuffle.
spark shuffle本质:根据分区分组、join等算子的条件key(groupbykey,partitionBy key,join by key,repartition(n),coalesce(n,true)) 将处于多个分区的数据拉取出来,重新放到给定的一个或者多个分区下。
四. RDD
RDD 的创建方式主要有2种:
- 并行化(Parallelizing)一个已经存在与驱动程序(Driver Program)中的集合如set、list;
- 读取外部存储系统上的一个数据集,比如HDFS、Hive、HBase,或者任何提供了Hadoop InputFormat的数据源.也可以从本地读取 txt、csv 等数据集
RDD 的操作函数(operation)主要分为2种类型 Transformation 和 Action.
类别 | 函数 | 区别 |
---|---|---|
Transformation | Map,filter,groupBy,join, union,reduce,sort,partitionBy | 返回值还是 RDD ,不会马上 提交 Spark 集群运行 |
Action | count,collect,take,save, show | 返回值不是 RDD ,会形成 DAG 图,提交 Spark 集群运行 并立即返回结果 |
Transformation 操作不是马上提交 Spark 集群执行的,Spark 在遇到 Transformation 操作时只会记录需要这样的操作,并不会去执行,需要等到有 Action 操作的时候才会真正启动计算过程进行计算.针对每个 Action,Spark 会生成一个 Job, 从数据的创建开始,经过 Transformation, 结尾是 Action 操作.这些操作对应形成一个有向无环图(DAG),形成 DAG 的先决条件是最后的函数操作是一个Action.
五.spark2.x shuffle
bucket的数量=cpu*resultTask的个数
版本二设计的原理:一个shuffleMapTask还是会写入resultTask对应个数的本地文件,但是当下一个shuffleMapTask运行的时候会直接把数据写到之前已经建立好的本地文件,这个文件可以复用,这种复用机制叫做consolidation机制
我们把这一组的shuffle文件称为shuffleGroup,每个文件中都存储了很多shuffleMapTask对应的数据,这个文件叫做segment,这个时候因为不同的shuffleMapTask都是存在一个文件中
所以建立索引文件,来标记shuffleMapTask在shuffleBlockFile的位置+偏移量,这样就可以在一个文件里面把不同的shuffleMaptask数据分出来
六. coalesce(n,true),repartition() 与 partitionby()
都是对数据进行重新分区,分区算法默认都是使用 HashPartitioner
七. DF的几种save Table 方式的比较:
7.1 df.write.mode(SaveMode.Append).saveAsTable("testDB.testTableName")
可以直接将df 保存在一个新的hive表中(不分区不需要事先在hive用语句建表),df中有多少分区,hive中就有多少分区,没有分区key
7.2 df.repartition(n).write.mode(SaveMode.Append).saveAsTable("testDB.testTableName")
df.repartition(n).write.mode(SaveMode.Overwrite ).saveAsTable("testDB.testTableName") 可以直接将df 保存在一个新的hive表中(因为这种方式的RDD的分区的key是随机生成的,所以保存到hive表中时并不会真正分区,所以也不需要事先在hive用语句建表),且 对df 重新分区,分区数为n,各个分区的key 随机生成。
注意:SaveMode.Overwrite 写入模式:不支持partitionby即
df.write.mode(SaveMode.Overwrite).partitionby("year","month","day").saveAsTable("testDB.testTableName") 会报错,即使事先在hive用语句建了分区表也不行,因为Overwrite 会先删除hive分区表,再重新建表,重新建表的时候不能自动建分区表,就会报错
7.3 df.repartition(n).write.mode(SaveMode.Append).partitionby("year","month","day").saveAsTable("testDB.testTableName")
可以将df 保存在一个新的hive表中(但是需要事先在hive用语句建分区表,根据partitionby的参数分区,为什么要这样做呢?DATAFRAM.SaveTestRead创建RDD分区,而不是HiVE分区。解决方法是在调用DataFrame.saveAsTable之前通过HQL创建表。),该语句会 对df 重新分区,分区数为n,各个分区的key 随机生成,然后再按年月日分区。即 把df 的数据 先按HashPartitioner算法 ,分布在n个分区中,再对每一个分区 按年月日分为 更小的分区。
7.4 df.write.mode(SaveMode.Append).partitionby("year","month","day").saveAsTable("testDB.testTableName")
可以将df 保存在一个新的hive表中(但是需要事先在hive用语句建分区表,根据partitionby的参数分区) 。即把df 的数据 按HashPartitioner算法 按年月日分为 更小的分区。df中有多少天的数据就有多少个分区。
对7.1 到7.4 为什么不用 coalesce(n)?因为 在spark1.6中coalesce(n) 默认shuffle=false用于调小分区数,而我们并不是很清楚df 本身的分区数,所以除了将n设为1,其他都有可能将分区调大,而coalesce(n) 方法是不能调大分区数的,因为他不能进行shuffle操作。当然如果你清楚了df的分区数,且希望调小分区,你最好是采用coalesce(n),因为它不需要进行shuffle操作,所以save操作耗时更短。
7.3 与7.4 的比较: 1. 前者最终的分区数>=后者的分区数,一般情况下 大于,特殊情况等于,因为分区数越多,存储的并行度越高,所以save操作耗时更短。相反根据年月日拉取save后的结果表后者会更快,因为后者同一天的数据都在一个分区中,而前者可能在多个分区中。
总结:
为什么要重新分区再 保存结果?
想避免小文件。如果不重新分区的话,SparkSQL DataFrame保存到hdfs时出现过多小文件问题。
因为多线程并行往hdfs写造成的(因为每个DataFrame/RDD分成若干个Partition,这些partition可以被并行处理)。
其结果就是一个存下来的文件,其实是hdfs中一个目录,在这个目录下才是众多partition对应的文件,最坏的情况是出现好多size为0的文件。
save Table 效率排名(分区数越多,效率越高,速度越快 ):
在n<天数的情况下 7.3>7.4>7.2 和7.1
效率:repartition(1)>repartition(5)>repartition(10)
coalesce(1)>coalesce(2)>coalesce(5)
coalesce>repartition
读取save后表的效率:分区数越多,执行线程并行度越高,效率越高,速度越快
最佳实践:
sqlhiveContext.sql("set hive.exec.dynamic.partition.mode=nonstrict")
sqlhiveContext.sql("set hive.exec.dynamic.partition=true")
//按下面方法save可以避免本次执行出现小文件过多的情况:
df.coalesce(1).write.mode(SaveMode.Append).partitionby("year","month","day").saveAsTable("testDB.testTableName")
df.repartition(1).write.mode(SaveMode.Append).partitionby("year","month","day").saveAsTable("testDB.testTableName")
注意:虽然可以使本次执行避免出现小文件过多,但是如果执行的次数很多,每次结果的数据量也不大,也会出现文件过多的情况,这时需要定时对历史所有结果重新执行
df.coalesce(1).write.mode(SaveMode.Append).partitionby("year","month","day").saveAsTable("testDB.testTableName")
并删除原来的小文件集。
解析:先将df 的小文件合并(一般df都是某个逻辑之后的结果,肯定有很多分区内的小文件),合并为1个分区能最大限度的合并小文件,spark1.6中用 coalesce(1)而不用repartition(1)的原因是,前者不需要进行shuffle操作,节约运行时间,最后再将这一个分区 按年月日 分为 多个小的分区,有多少天 就有多少个分区。(注意:需要事先在hive用语句建分区表,根据partitionby的参数分区;因为这里的分区只是RDD的分区,而不能设置hive分区) 。 最后为什么要按年月日分区呢,一方面把分区数从1再次增大,RDD分区数越多 ,可以提高程序运行(save)的并行度;另一方面 保存到分区表之后,再通过年月日 来筛选过滤数据,以进行某些查询(逻辑),会大大提高效率,因为同一天的数据放在同一分区,同一分区的数据 在物理地址上相邻(基本保持顺序存储),查询速度会更快。
为什么会产生小文件?
因为spark 的.saveAsTable("test_yw.testX") 写数据到hdfs时,有多少个reduce,就会有多少个小文件,reduce是并行写数据到各个文件,这也是spark 写速度快,但是容易产生小文件的原因。
而mapReduce的 map之后溢出的文件会从不同的节点 最终被拉取到一个地方(节点)进行合并,合并为1个文件,然后再进行reduce 。这也是spark 与mapreduce 的区别之一。
例子:
val df = sqlHiveContext.createDataFrame(Seq((1, 2017, 11, 13, "zhangsan", 21), (0, 2018, 11, 14, "lisi", 18), (0, 2017, 11, 14, "lisi1", 18), (1, 2018, 11, 14, "lisi", 18), (0, 2017, 11, 14, "lisi", 18), (0, 2017, 11, 14, "lisi", 18)))
.toDF("a", "b", "c", "d", "e", "f")
df.cache()
df.show(false)
df.write.mode(SaveMode.Overwrite).partitionBy("b").saveAsTable("test_yw.testX")
df.repartition(1).write.mode(SaveMode.Overwrite).partitionBy("b").saveAsTable("test_yw.testX1")
执行后查看hdfs文件,结果如下:
+---+----+---+---+--------+---+
|a |b |c |d |e |f |
+---+----+---+---+--------+---+
|1 |2017|11 |13 |zhangsan|21 |
|0 |2018|11 |14 |lisi |18 |
|0 |2017|11 |14 |lisi1 |18 |
|1 |2018|11 |14 |lisi |18 |
|0 |2017|11 |14 |lisi |18 |
|0 |2017|11 |14 |lisi |18 |
+---+----+---+---+--------+---+
没有repartition(1),但是partitionBy("b"),b=2017的有4条记录,其在b=2017的分区中也有4个文件,b=2018的行有两条记录,其在b=2018的分区中也有两个文件。
[greesj2b@slave8 ~]$ hdfs dfs -ls /user/hive/warehouse/test_yw.db/testx
Found 5 items
-rw-r--r-- 3 260169 supergroup 0 2018-10-11 13:57 /user/hive/warehouse/test_yw.db/testx/_SUCCESS
-rw-r--r-- 3 260169 supergroup 514 2018-10-11 13:57 /user/hive/warehouse/test_yw.db/testx/_common_metadata
-rw-r--r-- 3 260169 supergroup 3956 2018-10-11 13:57 /user/hive/warehouse/test_yw.db/testx/_metadata
drwxr-xr-x - 260169 supergroup 0 2018-10-11 13:57 /user/hive/warehouse/test_yw.db/testx/b=2017
drwxr-xr-x - 260169 supergroup 0 2018-10-11 13:57 /user/hive/warehouse/test_yw.db/testx/b=2018
[greesj2b@slave8 ~]$ hdfs dfs -ls /user/hive/warehouse/test_yw.db/testx/b=2017
Found 4 items
-rw-r--r-- 3 260169 supergroup 1070 2018-10-11 13:57 /user/hive/warehouse/test_yw.db/testx/b=2017/part-r-00014-b33e1370-34d1-4913-85c9-17f127669c4e.gz.parquet
-rw-r--r-- 3 260169 supergroup 1055 2018-10-11 13:57 /user/hive/warehouse/test_yw.db/testx/b=2017/part-r-00044-b33e1370-34d1-4913-85c9-17f127669c4e.gz.parquet
-rw-r--r-- 3 260169 supergroup 1050 2018-10-11 13:57 /user/hive/warehouse/test_yw.db/testx/b=2017/part-r-00074-b33e1370-34d1-4913-85c9-17f127669c4e.gz.parquet
-rw-r--r-- 3 260169 supergroup 1050 2018-10-11 13:57 /user/hive/warehouse/test_yw.db/testx/b=2017/part-r-00089-b33e1370-34d1-4913-85c9-17f127669c4e.gz.parquet
[greesj2b@slave8 ~]$ hdfs dfs -ls /user/hive/warehouse/test_yw.db/testx/b=2018
Found 2 items
-rw-r--r-- 3 260169 supergroup 1050 2018-10-11 13:57 /user/hive/warehouse/test_yw.db/testx/b=2018/part-r-00029-b33e1370-34d1-4913-85c9-17f127669c4e.gz.parquet
-rw-r--r-- 3 260169 supergroup 1050 2018-10-11 13:57 /user/hive/warehouse/test_yw.db/testx/b=2018/part-r-00059-b33e1370-34d1-4913-85c9-17f127669c4e.gz.parquet
既repartition(1),又partitionBy("b")每个分区各只有一个小文件
[greesj2b@slave8 ~]$ hdfs dfs -ls /user/hive/warehouse/test_yw.db/testx1
Found 5 items
-rw-r--r-- 3 260169 supergroup 0 2018-10-11 14:00 /user/hive/warehouse/test_yw.db/testx1/_SUCCESS
-rw-r--r-- 3 260169 supergroup 514 2018-10-11 14:00 /user/hive/warehouse/test_yw.db/testx1/_common_metadata
-rw-r--r-- 3 260169 supergroup 1671 2018-10-11 14:00 /user/hive/warehouse/test_yw.db/testx1/_metadata
drwxr-xr-x - 260169 supergroup 0 2018-10-11 14:00 /user/hive/warehouse/test_yw.db/testx1/b=2017
drwxr-xr-x - 260169 supergroup 0 2018-10-11 14:00 /user/hive/warehouse/test_yw.db/testx1/b=2018
[greesj2b@slave8 ~]$ hdfs dfs -ls /user/hive/warehouse/test_yw.db/testx1/b=2017
Found 1 items
-rw-r--r-- 3 260169 supergroup 1270 2018-10-11 14:00 /user/hive/warehouse/test_yw.db/testx1/b=2017/part-r-00000-1d0246a7-da71-4a2c-af26-053ef3b4f176.gz.parquet
[greesj2b@slave8 ~]$ hdfs dfs -ls /user/hive/warehouse/test_yw.db/testx1/b=2018
Found 1 items
-rw-r--r-- 3 260169 supergroup 1205 2018-10-11 14:00 /user/hive/warehouse/test_yw.db/testx1/b=2018/part-r-00000-1d0246a7-da71-4a2c-af26-053ef3b4f176.gz.parquet
再执行一次df.repartition(1).write.mode(SaveMode.Overwrite).partitionBy("b").saveAsTable("test_yw.testX1"),会发生什么?
每个分区都新增了1个文件,这也验证了:虽然可以使本次执行避免出现小文件过多,但是如果执行的次数很多,每次结果的数据量也不大,也会出现文件过多的情况,这时需要定时对历史所有结果重新执行repartiton(1),partitionBy("b")
[greesj2b@slave8 ~]$ hdfs dfs -ls /user/hive/warehouse/test_yw.db/testx1/b=2018
Found 2 items
-rw-r--r-- 3 260169 supergroup 1205 2018-10-11 14:00 /user/hive/warehouse/test_yw.db/testx1/b=2018/part-r-00000-1d0246a7-da71-4a2c-af26-053ef3b4f176.gz.parquet
-rw-r--r-- 3 260169 supergroup 1205 2018-10-11 14:31 /user/hive/warehouse/test_yw.db/testx1/b=2018/part-r-00000-3661f709-97c7-4c0d-a357-e610af7ba2a1.gz.parquet
[greesj2b@slave8 ~]$ hdfs dfs -ls /user/hive/warehouse/test_yw.db/testx1/b=2017
Found 2 items
-rw-r--r-- 3 260169 supergroup 1270 2018-10-11 14:00 /user/hive/warehouse/test_yw.db/testx1/b=2017/part-r-00000-1d0246a7-da71-4a2c-af26-053ef3b4f176.gz.parquet
-rw-r--r-- 3 260169 supergroup 1270 2018-10-11 14:31 /user/hive/warehouse/test_yw.db/testx1/b=2017/part-r-00000-3661f709-97c7-4c0d-a357-e610af7ba2a1.gz.parquet
[greesj2b@slave8 ~]$
既无repartition(1),又无partitionBy("b")
df.write.mode(SaveMode.Append).saveAsTable("test_yw.testX2") (有91个小文件)
Found 91 items
-rw-r--r-- 3 260169 supergroup 0 2018-10-11 14:15 /user/hive/warehouse/test_yw.db/testx2/_SUCCESS
-rw-r--r-- 3 260169 supergroup 582 2018-10-11 14:15 /user/hive/warehouse/test_yw.db/testx2/part-r-00000-ca1d1b08-798b-4e3f-a5f4-3d1bbaa45266.gz.parquet
-rw-r--r-- 3 260169 supergroup 582 2018-10-11 14:15 /user/hive/warehouse/test_yw.db/testx2/part-r-00001-ca1d1b08-798b-4e3f-a5f4-3d1bbaa45266.gz.parquet
-rw-r--r-- 3 260169 supergroup 582 2018-10-11 14:15 /user/hive/warehouse/test_yw.db/testx2/part-r-00002-ca1d1b08-798b-4e3f-a5f4-3d1bbaa45266.gz.parquet
-rw-r--r-- 3 260169 supergroup 582 2018-10-11 14:15 /user/hive/warehouse/test_yw.db/testx2/part-r-00003-ca1d1b08-798b-4e3f-a5f4-3d1bbaa45266.gz.parquet
-rw-r--r-- 3 260169 supergroup 582 2018-10-11 14:15 /user/hive/warehouse/test_yw.db/testx2/part-r-00004-ca1d1b08-798b-4e3f-a5f4-3d1bbaa45266.gz.parquet
-rw-r--r-- 3 260169 supergroup 582 2018-10-11 14:15 /user/hive/warehouse/test_yw.db/testx2/part-r-00005-ca1d1b08-798b-4e3f-a5f4-3d1bbaa45266.gz.parquet
八.Spark的并行度指的是什么?
1. spark作业中,各个stage的task的数量,也就代表了spark作业在各个阶段stage的并行度!
当分配完所能分配的最大资源了,然后对应资源去调节程序的并行度,如果并行度没有与资源相匹配,那么导致你分配下去的资源都浪费掉了。同时并行运行,还可以让每个task要处理的数量变少(很简单的原理。合理设置并行度,可以充分利用集群资源,减少每个task处理数据量,而增加性能加快运行速度。)
2、如何设置一个Spark Application的并行度?
spark.defalut.parallelism 默认是没有值的,如果设置了值比如说10,是在shuffle的过程才会起作用(val rdd2 = rdd1.reduceByKey(_+_) //rdd2的分区数就是10,rdd1的分区数不受这个参数的影响)
// 如果提交脚本中设置了20个executor ,每个executor 有4个core,那么就设置成20*4 的2到3倍
new SparkConf().set("spark.defalut.parallelism",''160'')//
.set("spark.executor.memory",''15g'')
九. spark 提交语句
1. 提交参数的设置既可以在提交脚本里面设置,也可以在spark 程序里面设
xx.sh
spark-submit --master yarn-client --class com.gree.test.Test --driver-memory 4g --num-executors 20 --executor-cores 4 --executor-memory 15g --driver-java-options '-Xms5g -Xmx5g -XX:+UseCompressedOops' --conf spark.driver.maxResultSize=2g --conf spark.executor.heartbeatInterval=15s --conf spark.yarn.executor.memoryOverhead=3072 --conf spark.memory.useLegacyMode=true --conf spark.shuff le.memoryFraction=0.5 --conf spark.storage.memoryFraction=0.3 --conf "spark.executor.extraJavaOptions=-XX:+UseG1GC -xx:InitiatingHeapOccupancyPercent=25 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps" --queue production/home/..../mysubmit/xxx.jar
提交参数说明:
./bin/spark-submit \
--class <main-class>
--master <master-url> \
--deploy-mode <deploy-mode> \
--conf <key>=<value> \
... # other options
<application-jar> \
[application-arguments]
--class
: The entry point for your application (e.g.org.apache.spark.examples.SparkPi
)--master
: The master URL for the cluster (e.g.spark://23.195.26.187:7077
)--deploy-mode
: Whether to deploy your driver on the worker nodes (cluster
) or locally as an external client (client
) (default:client
) †--conf
: Arbitrary Spark configuration property in key=value format. For values that contain spaces wrap “key=value” in quotes (as shown).application-jar
: Path to a bundled jar including your application and all dependencies. The URL must be globally visible inside of your cluster, for instance, anhdfs://
path or afile://
path that is present on all nodes.application-arguments
: Arguments passed to the main method of your main class, if any
详见:https://blog.csdn.net/stardhb/article/details/50454680
在spark scala 里面设置:
val conf =new SparkConf().setAppName("testProject").setMaster("yarn-client")
//.setMaster("spark://bakmaster:7077")//standalong
//.setMaster("local[*]")
//.setMaster("local[6]")
//.setMaster("local")
//.setMaster("yarn-cluster")//集群的客户端模式
//.setMaster("mesos://HOST:5050")//集群模式
// pg/gp配置
--driver-class-path /home/spark_submit/dbDriverJar/postgresql-42.2.2.jar
注意:需要将该驱动上传到各个执行器节点。
postgresql-42.2.2.jar下载地址:https://jdbc.postgresql.org/download.html
spark-submit
--master yarn
--class com.test.spark_testcdh62
--driver-class-path /home/spark_submit/dbDriverJar/postgresql-42.2.2.jar
--driver-memory 2g --num-executors 3 --executor-cores 8 --executor-memory 2g
--conf spark.driver.maxResultSize=2g
--conf spark.executor.heartbeatInterval=15s
--conf spark.executor.memoryOverhead=3072
--conf spark.memory.useLegacyMode=true
--conf spark.shuffle.memoryFraction=0.5
--conf spark.storage.memoryFraction=0.3
--queue production /home/spark_submit/sparkTest.jar
// mysql配置
conf.set("spark.driver.extraClassPath","/opt/cm-5.7.1/share/cmf/lib/mysql-connector-java-5.1.33-bin.jar")
conf.set("spark.executor.extraClassPath","/opt/cm-5.7.1/share/cmf/lib/mysql-connector-java-5.1.33-bin.jar")
// sqlserver配置
conf.set("spark.driver.extraClassPath","/opt/cloudera-manager/cm-5.7.1/share/cmf/lib/sqljdbc4.jar")
conf.set("spark.executor.extraClassPath","/opt/cloudera-manager/cm-5.7.1/share/cmf/lib/sqljdbc4.jar")
// oracle 配置
conf.set("spark.driver.extraClassPath","/opt/cloudera-manager/cm-5.7.1/share/cmf/lib/ojdbc6.jar")
conf.set("spark.executor.extraClassPath","/opt/cloudera-manager/cm-5.7.1/share/cmf/lib/ojdbc6.jar")
// 提交参数的配置
/*conf.set("spark.locality.wait.process","300s")
conf.set("spark.locality.wait.node","200s")
conf.set("spark.default.parallelism","300")
conf.set("spark.executor.memory","20g")
conf.set("spark.cores.max","100")
*/
2.提交参数的配置生效的优先级:
conf.set("key","value")> spark-submit>spark-defaults.conf
spark 调优 参考:
https://blog.csdn.net/vinfly_li/article/details/79415342
spark 参数主动调优:
1.从数据读取方面:
1.1 聚合input的小文件,防止小文件过多产生太多的task
spark.hadoop.mapreduce.input.fileinputformat.split.minsize(一个maptask处理的最小文件大小)
1.2 合并小文件到一个分区,默认值为4M,防止单个小文件占用一个分区。
spark.sql.files.opencostInBytes
1.3 如果不手动设置用于读取文件的分区个数且文件数量非常少且小,则默认最小值2个分区。
读取大致过程如下:
getSplits首先会拿到所有需要读取的file列表,然后会迭代这个file列表,首先看一个file是否可以再分即isSplitable(默认是true可能被子类覆盖),如果不能再split则直接作为1个split,如果可以再split,则获取这个file的block信息,然后综合根据多个参数来计算出1个split的数据大小即splitSize,然后会将这个file的所有block划分为多个split,划分过程会考虑机架、host等因素,如果是大block,则直接作为一个split,如果是小block可能多个block合并在一个split里(这样能够尽量减少split数量),最终得到的split数量即partition数量;
2. 从sql执行方面调优:
2.1 Join时 控制使用广播表的大小阈值,适当增大可以使得更多表走广播以提升性能,但设置太大又会造成driver端的内存压力,broadcastTimeOut用于控制超时时间,默认300ms。
spark.sql.autoBroadcastJoinThreshold && spark.sql.broadcastTimeOut
2.2 开启spark自适应执行
spark.sql.adaptive.enabled
2.3 控制shuffle阶段的平均输入数据大小,防止产生过多或过少task
spark.sql.adaptive.shuffle.targetPostShuffleInputSize
2.4 开启shuffle 自适应分区个数设置,以防止shuffle分区过少或过多 发生数据倾斜或运行缓慢
新版本期待
2.5 忽略空splits(分区),减少task数量
spark.hadoopRDD.ignoreEmptySplits
3. 写方面调优:
3.1 对最终结果重分区,以减少小文件个数(小文件个数太多会给driver端带来很大的压力)
3.2