spark的优化-控制数据分区和分布

数据分区:

在分布式集群里,网络通信的代价很大,减少网络传输可以极大提升性能。

mapreduce框架的性能开支主要在io和网络传输,io因为要大量读写文件,它是不可避免的,但是网络传输是可以避免的,把大文件压缩变小文件,从而减少网络传输,但是增加了cpu的计算负载。

spark里面io也是不可避免的,但是网络传输spark里面进行了优化:

spark把rdd进行分区(分片),放在集群上并行计算。

同一个rdd分片100个,10个节点,平均一个节点10个分区

当进行sum型的计算的时候,先进行每个分区的sum,然后把sum值shuffle传输到主程序进行全局sum,所以进行sum型计算对网络传输非常小。

但对于进行join型的计算的时候,需要把数据本身进行shuffle,网络开销很大。

spark是如何优化这个问题的呢?

spark把key-value rdd通过key的hashcode进行分区,而且保证相同的key存储在同一个节点上,这样对改rdd进行key聚合时,就不需要shuffle过程

我们进行mapreduce计算的时候为什么要尽兴shuffle?,就是说mapreduce里面网络传输主要在shuffle阶段,shuffle的根本原因是相同的key存在不同的节点上,按key进行聚合的时候不得不进行shuffle。shuffle是非常影响网络的,它要把所有的数据混在一起走网络,然后它才能把相同的key走到一起。要尽兴shuffle是存储决定的。

spark从这个教训中得到启发,spark会把key进行分区,也就是key的hashcode进行分区,相同的key,hashcode肯定是一样的,所以它进行分区的时候100t的数据分成10分,每部分10个t,它能确保相同的key肯定在一个分区里面,而且它能保证存储的时候相同的key能够存在同一个节点上。

比如一个rdd分成了100份,集群有10个节点,所以每个节点存10份,每一分称为每个分区,spark能保证相同的key存在同一个节点上,实际上相同的key存在同一个分区。

key的分布不均决定了有的分区大有的分区小。没法分区保证完全相等,但它会保证在一个接近的范围。

所以mapreduce里面做的某些工作里边,spark就不需要shuffle了,spark解决网络传输这块的根本原理就是这个。

进行join的时候是两个表,不可能把两个表都分区好,通常情况下是把用的频繁的大表事先进行分区,小表进行关联它的时候小表进行shuffle过程。

大表不需要shuffle。

模版是:

val userData = sc.sequenceFile[UserID,UserInfo]("hdfs://...")

.partitionBy(new HashPartition(100))//构造100个分区

.persist()

从分区中获益的操作:cogroup(), groupwith(),join(),leftOuterJoin(),rightOuterJoin(),groupByKey(),reduceByKey(),cobimeByKey(),lookup()

所有基于key的操作都会获益

对于诸如cogroup()和join()这样的二元操作,预先进行数据分区会让其中至少一个rdd(使用已知分区器的那个rdd)不发生数据shuffle,如果两个rdd使用同样的分区方式,并且它们还缓存在同样的机器上(比如一个rdd是通过mapvalues()从另一个rdd中创建出来的,这两个rdd就会拥有相同的key和分区方式),或者其中rdd还没有被计算出来,那么跨界点的shuffle(数据混洗)不会发生了。

mapreduce一般要求本地网卡达到20兆!即便进行了压缩!

代码:

import org.apache.hadoop.hive.ql.exec.persistence.HybridHashTableContainer.HashPartition
import org.apache.hadoop.mapred.lib
import org.apache.spark.SparkConf
import org.apache.spark.sql.SparkSession
import org.apache.spark.storage.StorageLevel
import org.apache.spark.HashPartitioner
/**
  * Created by zengxiaosen on 16/9/23.
  */
object PartitionVisitCount {

  /*
  大表小表关联
   */
  def main(args: Array[String]): Unit = {
    val sparkConf = new SparkConf().setAppName("useUDF").setMaster("local")
    val ss = SparkSession.builder().config(sparkConf).getOrCreate()
    val sc = ss.sparkContext

    val fileRDD = sc.textFile("/opt/tarballs/spark_kafka/beifengspark/src/main/scala/2015082818")
      .filter(line=>line.length>0)
      .map{
        line =>
          val arr = line.split("\t")
          val date = arr(17).substring(0,10)
          val guid = arr(5)
          val url = arr(1)
          val uid = arr(18)
          (uid,(guid,url)) //key-value:tuple2
      }.partitionBy(new HashPartitioner(10)) //采用了hashcode分片方式,分成了10份,十个分区,每个分区10分
      /*
      相同的key在同一个分区,在进行任务调用时候,大表不需要任何shuffle
      只需要shuffle小表
       */
      .persist(StorageLevel.DISK_ONLY)


    /*
    parallelize有两个参数,第一个是他的list,第二个是分区数
    分区数可以不给,不给的情况下默认就是它的核数
     */
    //比如里面存的是我的用户id
    val rdd = sc.parallelize(List(1,2,3,4,5,6,7),10)
      .map(i => (i+"", i+"str"))

    fileRDD.join(rdd).foreach(println)
    /*
    如果fileRDD后面还会关联好多个其他的rdd1,rdd2。。。rddn
    就要先把大的fileRDD进行分区
    这样优化了网络传输

     */


  }

}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值