spark@分区操作

186 阅读1分钟
val data = sc.parallelize(1 to 20,2)

// 模拟一个场景 每一条数据都需要 去数据库拿点东西出来进行关联
// 这个写法要疯
data.map(e=>{
  println("connect ...")
  println(e)
  println("close ...")
}).foreach(println)
上面的写法是对每条数据进行操作
有没有一种对整个分区的数据进行操作的方法呢

mapPartitionsWithIndex

mapPartitionsWithIndex这个算子对一个partition的数据进行操作

// 错误代码 极大的消耗内存。
data.mapPartitionsWithIndex((index, pIter) => {
  var buffer = new ListBuffer[Int]()
  println(s"connect---$index")
  while (pIter.hasNext) {
    val i = pIter.next()
    println(i)
    buffer.+=(i)
  }
  println(s"close---$index")
  buffer.iterator
}).foreach(println)
  • 上面的代码使用分区的迭代器进行了数据的遍历并且返回了一个数据集,这样操作很危险,消耗内存!
// 利用迭代器
data.mapPartitionsWithIndex((index, pIter) => {
  println(s"$index connect ...")
  // 应该嵌套一个迭代器才对
  new Iterator[Int]() {
    override def hasNext: Boolean = {
      if (!pIter.hasNext) {
        println(s"$index close ...")
        false
      } else true
    }
    override def next(): Int = {
      val i = pIter.next()
      println(s"select $i")
      i
    }
  }
}).foreach(println)

mapPartitionsWithIndex和map的区别

  • 调用这两个方法本质上最后都是new出了mapPartitionsRDD
  • 下面来看区别 首先看mapPartitionsWithIndex里面的
// 这是mapPartitionsWithIndex里面的
new MapPartitionsRDD(
  this,
  (context: TaskContext, index: Int, iter: Iterator[T]) => cleanedF(index, iter),
  preservesPartitioning)

下面来看看map的

new MapPartitionsRDD[U, T](this, (context, pid, iter) => iter.map(cleanF))
  • 这两个转换算子都是返回了mapPartitionsRDD但是给mapPartitionsRDD的构造传入的方法不同! 这是区别,也解释了为什么mapPartitionsWithIndex能够对一个分区的数据进行操作。