Spark基础03-关联外部数据源操作

195 阅读1分钟

1、通过map关联外部数据源

val data: RDD[Int] = sc.parallelize(1 to 10, 2)

// 1、外关联、SQL查询
// 问题:每一条数据都要建立一个数据连接,执行SQL,然后关闭连接
val res01: RDD[String] = data.map(
  (value: Int) => {
    println("-----连接数据库-----")
    println(s"-----select $value-----")
    println("-----关闭数据库连接-----")
    value + " selected"
  }
)
    res01.foreach(println)

问题:每一条数据都会对数据库建立连接,执行SQL语句,关闭连接。频繁的建立关闭连接会造成大量的资源消耗

2、通过mapPartitionsWithIndex和mapPartitions关联外部数据源

// mapPartitions 实现方式与以下方法相似
val res2: RDD[String] = data.mapPartitionsWithIndex(
  (pindex, piter) => {
      
    // 需要返回Iterator
    val lb = new ListBuffer[String]

    println(s"-----$pindex----连接数据库-----")
    while (piter.hasNext) {
      val value: Int = piter.next()
      println(s"-----select $value-----")
      lb += (value + " selected")
    }
    println("-----关闭数据库连接-----")
    lb.iterator
  }
)
res2.foreach(println)

解决问题:基于分区对数据库建立连接,执行SQL语句,然后关闭连接,减少了建立连接的次数,降低资源的消耗

问题:运算过程中引入了新的迭代器,在分区过多及数据量过大的情况下,会在内存中积压大量数据,势必造成OOM

3、通过mapPartitionsWithIndex和mapPartitions关联外部数据源-采用迭代器嵌套模式【优化点】

val res03: RDD[String] = data.mapPartitionsWithIndex(

  (pindex, piter) => {

    // 需要返回Iterator
    // 迭代器嵌套,中间不出现积压内存情况
    new Iterator[String] {
      println(s"-----$pindex----连接数据库-----")

      override def hasNext = if (piter.hasNext == false) {
        println("-----关闭数据库连接-----")
        false
      } else true

      override def next() = {
        val value: Int = piter.next()
        println(s"-----select $value-----")
        value + " selected"
      }
    }
  }
)
res03.foreach(println)

解决问题:利用迭代器可嵌套的特性,解决缓存中间结果可能导致OOM问题

4、总结

  • spark在关联外部数据源时,建议使用mapPartitionsWithIndex和mapPartitions,而是不使用map,针对一整个分区操作,比针对每条数据操作快得多
  • 在使用mapPartitions时,推荐使用迭代器嵌套模式,这样既不会存在中间缓冲结果,又可以减少不必要的资源消耗