Spark 多线程并行提交job

优化背景

由于Driver的单线程运行以及Spark的任务调度决定了Job是串行执行的,但是当各个job之间的业务逻辑是相互独立的时候,我们就可以考虑多线程并行处理!

因为是测试: 以下代码 是单表(TMP)跑四次,实际情况下是多张表

串行处理

def main(args: Array[String]): Unit = {
    val watch = new StopWatch
    watch.start()
    val session: SparkSession = SparkSession.builder().appName(this.getClass.getSimpleName).master("local[10]")
      .getOrCreate()
    val df: DataFrame = session.read
      .json("file:///C:\\Users\\HR\\Desktop\\测试数据\\courseshoppingcart.log")
    val df2: Dataset[Row] = df.coalesce(8)
    df2.createOrReplaceTempView("TMP")

    val list = List(
      "select * from TMP where courseid=103",
      "select * from TMP where courseid=101",
      "select * from TMP where courseid=9514",
      "select * from TMP where courseid=4894"
    )
    var sum=0L
    list.foreach(sql=>{
      sum+=session.sql(sql).count()
    })
    println(sum)
    watch.stop()
    println(watch.getTime)
    session.close()
  }

在这里插入图片描述

如上图所示:提交的四个任务是串行执行的

线程池并行处理

本地测试,本机10个cpu,4个job,每个job2个并行度也就是2个cpu。此时耗用8个cpu,所以不存在线程切换。

def main(args: Array[String]): Unit = {
    val watch = new StopWatch
    watch.start()
    val session: SparkSession = SparkSession.builder().appName(this.getClass.getSimpleName).master("local[10]")
      .getOrCreate()
    val df: DataFrame = session.read
      .json("file:///C:\\Users\\HR\\Desktop\\测试数据\\courseshoppingcart.log")
    val df2: Dataset[Row] = df.coalesce(2) // 2个分区
    df2.createOrReplaceTempView("TMP")

    var executor: ExecutorService = null
    try {
      val list = List(
        "select * from TMP where courseid=103",
        "select * from TMP where courseid=101",
        "select * from TMP where courseid=9514",
        "select * from TMP where courseid=4894"
      )
      // 创建线程池
      executor = Executors.newWorkStealingPool(4)
      // 创建future以便于接收返回值
      val futureList = new util.ArrayList[Future[Integer]](4)
      list.foreach(sql => {
        val callable: Callable[Integer] = new Callable[Integer]() {
          override def call() = {
            session.sql(sql).count().toInt
          }
        }
        //接收返回值
        futureList.add(executor.submit(callable))
      })

      var sum = 0
      // 累加返回值
      futureList.forEach(future => {
        val res: Int = future.get().toInt //get是阻塞方法
        sum += res
      })
      println(sum)
    } finally {
      executor.shutdown()
      session.close()
    }
    watch.stop()
    println(watch.getTime)
  }

在这里插入图片描述
在这里插入图片描述

可以看到,job之间并行处理,在cpu资源充足的情况下,可以很大的提升任务效率!

小结

  1. 多线程提交任务时,当资源申请足够多时,会同时执行,即使资源不足,也会在上一个任务结束释放资源后立即执行
  2. 哪些动作是需要统一做的,哪些是需要并行执行的,在哪里做Cache来应对Lazy执行带来的问题
  3. 合理提升Executor的内存来保证并行执行过程中内存是够用的
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值