Spark_Spark算子_repartitionAndSortWithinPartitions

 

SparkにはrepartitionAndSortWithinPartitions演算子が用意されています。最初に、この演算子の有用性について説明します。

オペレーターは、指定されたパーティショナーごとにグループ化し、グループ内でソートできます。

したがって、次のようにしてニーズを満たすことができます。

たとえば、次のとおりです。

   例1. rddデータ同じクラスの生徒パーティションに分割しスコアの降順でソートする

   例2.同じ組み合わせKeyが同じパーティションにグループ化され、パーティションは最初にKEYでソートされ、同じキーが他のキーでソートされます

 

まずは公式サイトから機能紹介をご覧ください:

アドレス:http : //spark.apache.org/docs/latest/rdd-programming-guide.html#working-with-key-value-pairs

 

repartitionAndSortWithinPartitionsパーティション 指定されたパーティショナーに従ってRDDを再パーティション化し、結果の各パーティション内で、レコードをキーでソートします。これは、repartition 各パーティション内で呼び出してから並べ替えるよりも効率的です。これは  、並べ替えをシャッフル機構に押し下げることができるためです。

  repartitionAndSortWithinPartitionsは、主に同じKEYの要素を、指定されたパーティショナーを介して指定されたパーティションに送信し、KEYに従ってソートすることがわかります。ヒント:カスタムの並べ替えルールに従って2次並べ替えを実行できます。

  さらに、repartitionAndSortWithinPartitionsは効率的な演算子であり、最初にrepartitionを呼び出してからグループ内で並べ替えるよりも効率的です。これは、並べ替えがシャッフルと並べ替えの間にシャッフルプロセス中に実行されるためです。詳細はSpark shuffleをご覧ください  。読み出し動作

 

ソースコードを大まかに見てみましょう:

def sortByKey(ascending: Boolean = true, numPartitions: Int = self.partitions.length)
      : RDD[(K, V)] = self.withScope
  {
    val part = new RangePartitioner(numPartitions, self, ascending)
    new ShuffledRDD[K, V, V](self, part)
      .setKeyOrdering(if (ascending) ordering else ordering.reverse)
  }

  /**
   * Repartition the RDD according to the given partitioner and, within each resulting partition,
   * sort records by their keys.
   *
   * This is more efficient than calling `repartition` and then sorting within each partition
   * because it can push the sorting down into the shuffle machinery.
   */
 def repartitionAndSortWithinPartitions(partitioner: Partitioner): RDD[(K, V)] = self.withScope {
    new ShuffledRDD[K, V, V](self, partitioner).setKeyOrdering(ordering)
  }

 

特定の要件でこの演算子を使用する方法を見てみましょう。

 例1. rddデータ同じクラスの生徒パーティションに分割しスコアの降順でソートする

実装コード

package com.gaosi.spark.demo

/**
  * Created by szh on 2019/9/19.
  */

import org.apache.spark.{SparkConf, SparkContext}


class Student {

}


//创建key类,key组合键为grade,score
case class StudentKey(grade: String, score: Int)
//  extends Ordered[StudentKey] {
//  def compare(that: StudentKey): Int = {
//    var result: Int = this.grade.compareTo(that.grade)
//    if (result == 0) {
//      result = that.score.compareTo(this.score)
//    }
//    result
//  }
//}

object StudentKey {
  implicit def orderingByGradeStudentScore[A <: StudentKey]: Ordering[A] = {
    Ordering.by(fk => (fk.grade, fk.score * -1))
  }
}

//创建分区类
import org.apache.spark.Partitioner

class StudentPartitioner(partitions: Int) extends Partitioner {
  require(partitions >= 0, s"Number of partitions ($partitions) cannot be negative.")

  override def numPartitions: Int = partitions

  override def getPartition(key: Any): Int = {
    val k = key.asInstanceOf[StudentKey]
    Math.abs(k.grade.hashCode()) % numPartitions
  }
}

object Student {


  def main(args: Array[String]) {


    //定义hdfs文件索引值
    val grade_idx: Int = 0
    val student_idx: Int = 1
    val course_idx: Int = 2
    val score_idx: Int = 3

    //定义转化函数,不能转化为Int类型的,给默认值0
    def safeInt(s: String): Int = try {
      s.toInt
    } catch {
      case _: Throwable => 0
    }

    //定义提取key的函数
    def createKey(data: Array[String]): StudentKey = {
      StudentKey(data(grade_idx), safeInt(data(score_idx)))
    }

    //定义提取value的函数
    def listData(data: Array[String]): List[String] = {
      List(data(grade_idx), data(student_idx), data(course_idx), data(score_idx))
    }

    def createKeyValueTuple(data: Array[String]): (StudentKey, List[String]) = {
      (createKey(data), listData(data))
    }



    //设置master为local,用来进行本地调试
    val conf = new SparkConf().setAppName("Student_partition_sort").setMaster("local")
    val sc = new SparkContext(conf)

    //学生信息是打乱的
    val student_array = Array(
      "c001,n003,chinese,59",
      "c002,n004,english,79",
      "c002,n004,chinese,13",
      "c001,n001,english,88",
      "c001,n002,chinese,10",
      "c002,n006,chinese,29",
      "c001,n001,chinese,54",
      "c001,n002,english,32",
      "c001,n003,english,43",
      "c002,n005,english,80",
      "c002,n005,chinese,48",
      "c002,n006,english,69"
    )
    //将学生信息并行化为rdd
    val student_rdd = sc.parallelize(student_array)
    //生成key-value格式的rdd
    val student_rdd2 = student_rdd.map(line => line.split(",")).map(createKeyValueTuple)
    //根据StudentKey中的grade进行分区,并根据score降序排列
    val student_rdd3 = student_rdd2.repartitionAndSortWithinPartitions(new StudentPartitioner(10))
    //打印数据
    student_rdd3.collect.foreach(println)
  }
}

アウトプット 

(StudentKey(c001,88)、List(c001、n001、english、88))
(StudentKey(c001,59)、List(c001、n003、chinese、59))
(StudentKey(c001,54)、List(c001、 n001、chinese、54))
(StudentKey(c001,43)、List(c001、n003、english、43))
(StudentKey(c001,32)、List(c001、n002、english、32))
(StudentKey(c001、 10)、List(c001、n002、chinese、10))
(StudentKey(c002,80)、List(c002、n005、english、80))
(StudentKey(c002,79)、List(c002、n004、english、79 ))
(StudentKey(c002,69)、List(c002、n006、english、69)))
(StudentKey(c002,48)、List(c002、n005、chinese、48))
(StudentKey(c002,29)、List( c002、n006、chinese、29))
(StudentKey(c002,13)、List(c002、n004、chinese、13))

 

 

ここでは2つの点に注意します。

一つはパーティショナーです

import org.apache.spark.Partitioner

class StudentPartitioner(partitions: Int) extends Partitioner {
  require(partitions >= 0, s"Number of partitions ($partitions) cannot be negative.")

  override def numPartitions: Int = partitions

  override def getPartition(key: Any): Int = {
    val k = key.asInstanceOf[StudentKey]
    Math.abs(k.grade.hashCode()) % numPartitions
  }
}

hashCodeは負の値になる可能性があるので、Math.absを呼び出してください。

 

 

2番目のポイントは、並べ替えの実装です 

object StudentKey {
  implicit def orderingByGradeStudentScore[A <: StudentKey]: Ordering[A] = {
    Ordering.by(fk => (fk.grade, fk.score * -1))
  }
}

 

发布了519 篇原创文章 · 获赞 1146 · 访问量 283万+

おすすめ

転載: blog.csdn.net/u010003835/article/details/101000077