mapPartitions

package com.shujia.core

import com.shujia.core.Demo10Join.Student
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

import java.sql.{Connection, DriverManager, PreparedStatement, ResultSet}
import scala.collection.mutable.ListBuffer

object Demo18MapPartitions {
  def main(args: Array[String]): Unit = {
    println("============Spark程序开始运行============")
    // RDD是由一系列分区组成的
    // Spark会提供最佳计算位置 移动计算 不移动数据
    /**
     * 可以将Spark代码分为两个部分
     * Driver端执行的
     * Task中执行的
     */
    val conf: SparkConf = new SparkConf()
    conf.setAppName("Demo18MapPartitions")
    conf.setMaster("local")

    val sc: SparkContext = new SparkContext(conf)

    // 读取学生数据及分数数据 并将每一行数据转换成样例类对象
    //    val stuRDD: RDD[Student] = sc.parallelize(List(
    //      Student("1500100001", "zs", 20, "男", "文科一班"),
    //      Student("1500100002", "ls", 21, "女", "文科三班"),
    //      Student("1500100003", "ww", 23, "男", "文科二班"),
    //      Student("1500100004", "ll", 22, "男", "文科一班")
    //    ))

    val stuRDD: RDD[Student] = sc
      .textFile("Spark/data/students.txt")
      .map(line => {
        val splits: Array[String] = line.split(",")
        val id: String = splits(0)
        val name: String = splits(1)
        val age: Int = splits(2).toInt
        val gender: String = splits(3)
        val clazz: String = splits(4)
        Student(id, name, age, gender, clazz)
      })
    
    // 遍历RDD中的每个学生数据 取id 并作为PreparedStatement的参数
    stuRDD

      /**
       * flatMap中的逻辑 会被封装成Task 发送到Executor中执行
       * 在flatMap中传入的逻辑需要能够进行序列化
       * 在这里由于MySQL的连接是不能被序列化的
       */
      .flatMap(stu => {
        // 算子外部的代码会在Driver执行 算子内部的代码会被以Task形式发送到Executor中执行
        // 由于MySQL的连接不能被序列化的 所以需要将建立连接的过程放每一个Task中
        // 如果直接使用map 或者是 flatMap 或者 foreach 去遍历RDD中的每条数据
        // 那么会造成 每条数据都需要建立一次连接 建立和销毁连接的过程是非常耗时的
        // 从远程数据库MySQL 获取分数表score数据 并实现关联
        // 建立MySQL连接
        val conn: Connection = DriverManager.getConnection("jdbc:mysql://rm-bp1h7v927zia3t8iwho.mysql.rds.aliyuncs.com:3306/stu016?useSSL=false", "shujia016", "123456")
        // 创建Statement
        val pSt: PreparedStatement = conn.prepareStatement("select course_id,score from score where student_id = ?")

        val id: String = stu.id
        // 设置SQL参数
        pSt.setInt(1, id.toInt)
        // 执行查询语句
        val rs: ResultSet = pSt.executeQuery()
        val stuListBF: ListBuffer[String] = ListBuffer[String]()

        // 将每个学生的6门成绩加入ListBuffer中 并使用flatMap展开
        while (rs.next()) {
          val score_id: Int = rs.getInt("course_id")
          val score: Int = rs.getInt("score")
          stuListBF.append(s"$id,${stu.name},$score_id,$score")
        }
        // 关闭连接
        pSt.close()
        conn.close()

        stuListBF
      })
    //      .foreach(println)

    // 使用mapPartitions 或者 foreachPartitions 代替map/flatMap/foreach

    stuRDD
      .mapPartitions(stuIter => {
        // mapPartitions 会对每个分区进行遍历
        // 每个分区 即 每个Task任务 会建立一次连接 避免每条数据建立连接
        // 从远程数据库MySQL 获取分数表score数据 并实现关联
        // 建立MySQL连接
        println("创建MySQL连接")
        val conn: Connection = DriverManager.getConnection("jdbc:mysql://rm-bp1h7v927zia3t8iwho.mysql.rds.aliyuncs.com:3306/stu016?useSSL=false", "shujia016", "123456")
        // 创建Statement
        val pSt: PreparedStatement = conn.prepareStatement("select course_id,score from score where student_id = ?")

        val stuScoreIter: Iterator[String] = stuIter
          .flatMap(stu => {
            val id: String = stu.id
            // 设置SQL参数
            pSt.setInt(1, id.toInt)
            // 执行查询语句
            val rs: ResultSet = pSt.executeQuery()
            val stuListBF: ListBuffer[String] = ListBuffer[String]()

            // 将每个学生的6门成绩加入ListBuffer中 并使用flatMap展开
            while (rs.next()) {
              val score_id: Int = rs.getInt("course_id")
              val score: Int = rs.getInt("score")
              stuListBF.append(s"$id,${stu.name},$score_id,$score")
            }
            stuListBF
          })
        stuScoreIter
      })
    //      .foreach(println)

    val intRDD: RDD[Int] = sc.parallelize(List(1, 2, 3, 4, 5), 2)
    intRDD
      .mapPartitionsWithIndex((index, iter) => {
        println(s"当前遍历的分区为:$index")
        iter
          .map(i => i * i)
      }).foreach(println)

    println("============Spark程序运行结束============")
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
⼤数据⾼频⾯试题 ⼤数据⾼频⾯试题 ⾯试中的问题(重点)** 1. RDD的特性(RDD的解释) 1.RDD可以看做是⼀些列partition所组成的 2.RDD之间的依赖关系 3.算⼦是作⽤在partition之上的 4.分区器是作⽤在kv形式的RDD上 5.partition提供的最佳计算位置,利于数据处理的本地化即计算向数据移动⽽不是移动数据 ps:RDD本⾝是不存储数据,可以看做RDD本⾝是⼀个引⽤数据 RDD弹性 1) ⾃动进⾏内存和磁盘数据存储的切换 Spark优先把数据放到内存中,如果内存放不下,就会放到磁盘⾥⾯,程序进⾏⾃动的存储切换 2) 基于⾎统的⾼效容错机制 在RDD进⾏转换和动作的时候,会形成RDD的Lineage依赖链,当某⼀个RDD失效的时候,可以通过重新计算上游的RDD来重新⽣成丢失 的RDD数据。 3) Task如果失败会⾃动进⾏特定次数的重试 RDD的计算任务如果运⾏失败,会⾃动进⾏任务的重新计算,默认次数是4次。 4) Stage如果失败会⾃动进⾏特定次数的重试 如果Job的某个Stage阶段计算失败,框架也会⾃动进⾏任务的重新计算,默认次数也是4次。 5) Checkpoint和Persist可主动或被动触发 RDD可以通过Persist持久化将RDD缓存到内存或者磁盘,当再次⽤到该RDD时直接读取就⾏。也可以将RDD进⾏检查点,检查点会将数据 存储在HDFS中,该RDD的所有⽗RDD依赖都会被移除。 6) 数据调度弹性 Spark把这个JOB执⾏模型抽象为通⽤的有向⽆环图DAG,可以将多Stage的任务串联或并⾏执⾏,调度引擎⾃动处理Stage的失败以及Task 的失败。 7) 数据分⽚的⾼度弹性 可以根据业务的特征,动态调整数据分⽚的个数,提升整体的应⽤执⾏效率。 2. RDD的两类算⼦ RDD编程API RDD⽀持两种操作:转化操作和⾏动操作。RDD 的转化操作是返回⼀个新的 RDD的操作,⽐如 map()和 filter(),⽽⾏动操作则是向驱动器程 序返回结果或把结果写⼊外部系统的操作。⽐如 count() 和 first()。 Spark采⽤惰性计算模式,RDD只有第⼀次在⼀个⾏动操作中⽤到时,才会真正计算。Spark可以优化整个计算过程。默认情况下,Spark 的 RDD 会在你每次对它们进⾏⾏动操作时重新计算。如果想在多个⾏动操作中重⽤同⼀个 RDD,可以使⽤ RDD.persist() 让 Spark 把这个 RDD 缓存下来。 3.25.17 Transformation算⼦(重要) RDD中的所有转换都是延迟加载的,也就是说,它们并不会直接计算结果。相反的,它们只是记住这些应⽤到基础数据集(例如⼀个⽂件) 上的转换动作。只有当发⽣⼀个要求返回结果给Driver的动作时,这些转换才会真正运⾏。这种设计让Spark更加有效率地运⾏。 转换 含义 map(func) 返回⼀个新的RDD,该RDD由每⼀个输⼊元素经过func函数转换后组成 filter(func) 返回⼀个新的RDD,该RDD由经过func函数计算后返回值为true的输⼊元素组成 flatMap(func) 类似于map,但是每⼀个输⼊元素可以被映射为0或多个输出元素(所以func应该返回⼀个序列,⽽不是单⼀元素) mapPartitions(func) 类似于map,但独⽴地在RDD的每⼀个分⽚上运⾏,因此在类型为T的RDD上运⾏时,func的函数类型必须是Iterator[T] => Iterator[U] mapPartitionsWithIndex(func) 类似于mapPartitions,但func带有⼀个整数参数表⽰分⽚的索引值,因此在类型为T的RDD上运⾏时,func的 函数类型必须是(Int, Iterator[T]) => Iterator[U] sample(withReplacement, fraction, seed) 根据fraction指定的⽐例对数据进⾏采样,可以选择是否使⽤随机数进⾏替换,seed⽤于指定随机 数⽣成器种⼦ union(otherDataset) 对源RDD和参数RDD求并集后返回⼀个新的RDD intersection(otherDataset) 对源RDD和参数RDD求交集后返回⼀个新的RDD distinct([numTasks])) 对源RDD进⾏去重后返回⼀个新的RDD groupByKey([numTasks]) 在⼀个(K,V)的RDD上调⽤,返回⼀个(K, Iterator[V])的RDD reduceByKey(func, [numTasks]) 在⼀个(K,V)的RDD上调⽤,返回⼀个(K,V)的RDD,使⽤指定的reduce函数,将相同

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值