Cuándo es la dependencia amplia de la unión de Spark y cuándo es la dependencia estrecha

¿Cuándo es la dependencia amplia y la dependencia estrecha de la combinación de Spark?
Pregunta:
Consulte el siguiente código:
1. ¿Cuál es el resultado de las dos declaraciones de impresión, si la dependencia correspondiente es una dependencia amplia o una dependencia estrecha, y por qué es este resultado?
2. Operación de combinación Cuándo es la dependencia amplia y cuándo es la dependencia estrecha;


import org.apache.spark.rdd.RDD
import org.apache.spark.{
    
    HashPartitioner, SparkConf, SparkContext}
object JoinDemo2 {
    
    
  def main(args: Array[String]): Unit = {
    
    
    val conf = new SparkConf().setAppName(this.getClass.getCanonicalName.init).setMaster("local[*]")
    val sc = new SparkContext(conf)
    sc.setLogLevel("WARN")
    val random = scala.util.Random
    val col1 = Range(1, 50).map(idx => (random.nextInt(10), s"user$idx"))
    val col2 = Array((0, "BJ"), (1, "SH"), (2, "GZ"), (3, "SZ"), (4, "TJ"), (5, "CQ"), (6, "HZ"), (7, "NJ"), (8, "WH"), (0,
      "CD"))
    val rdd1: RDD[(Int, String)] = sc.makeRDD(col1)
    val rdd2: RDD[(Int, String)] = sc.makeRDD(col2)
    val rdd3: RDD[(Int, (String, String))] = rdd1.join(rdd2)
    rdd3.count()
    println(rdd3.dependencies)
    val rdd4: RDD[(Int, (String, String))] = rdd1.partitionBy(new HashPartitioner(3)).join(rdd2.partitionBy(new
        HashPartitioner(3)))
    rdd4.count()
    println(rdd4.dependencies)
    Thread.sleep(10000000)
    sc.stop()
  }}

Respuesta:
1. Dos declaraciones de impresión: List(org.apache.spark.OneToOneDependency@63acf8f6) List(org.apache.spark.OneToOneDependency@d9a498)
Inserte la descripción de la imagen aquí
Dependencias correspondientes:
rdd3 corresponde a dependencias amplias y rdd4 corresponde a dependencias estrechas.
Razones:
1) Consulte la interfaz
de usuario web del diagrama DAG. Se puede ver que la primera unión está claramente separada de la el anterior Satge. Puede verse que se trata de una dependencia amplia. La segunda unión, unión después de la partición por, no se divide en una etapa por separado, lo que muestra que es una dependencia estrecha.
rdd3 join
Inserte la descripción de la imagen aquí
rdd4 join
Inserte la descripción de la imagen aquí
2) Análisis de código:
a. Primero es el método de unión predeterminado, aquí se usa un particionador predeterminado

  /**
   * Return an RDD containing all pairs of elements with matching keys in `this` and `other`. Each
   * pair of elements will be returned as a (k, (v1, v2)) tuple, where (k, v1) is in `this` and
   * (k, v2) is in `other`. Performs a hash join across the cluster.
   */
  def join[W](other: RDD[(K, W)]): RDD[(K, (V, W))] = self.withScope {
    
    
    join(other, defaultPartitioner(self, other))
  }

b. El particionador predeterminado, para la primera unión, devolverá un HashPartitioner con el número total de núcleos de computadora como el número de particiones. La segunda unión devolverá el HashPartitioner que establecimos (partición número 3)

  def defaultPartitioner(rdd: RDD[_], others: RDD[_]*): Partitioner = {
    
    
    val rdds = (Seq(rdd) ++ others)
    val hasPartitioner = rdds.filter(_.partitioner.exists(_.numPartitions > 0))

    val hasMaxPartitioner: Option[RDD[_]] = if (hasPartitioner.nonEmpty) {
    
    
      Some(hasPartitioner.maxBy(_.partitions.length))
    } else {
    
    
      None
    }

    val defaultNumPartitions = if (rdd.context.conf.contains("spark.default.parallelism")) {
    
    
      rdd.context.defaultParallelism
    } else {
    
    
      rdds.map(_.partitions.length).max
    }

    // If the existing max partitioner is an eligible one, or its partitions number is larger
    // than the default number of partitions, use the existing partitioner.
    if (hasMaxPartitioner.nonEmpty && (isEligiblePartitioner(hasMaxPartitioner.get, rdds) ||
        defaultNumPartitions < hasMaxPartitioner.get.getNumPartitions)) {
    
    
      hasMaxPartitioner.get.partitioner.get
    } else {
    
    
      new HashPartitioner(defaultNumPartitions)
    }
  }

c. Vaya a la ejecución real del método de unión, en el que flatMapValues ​​es una dependencia estrecha, por lo que si hay una dependencia amplia, debería estar en el operador cogroup

  /**
   * Return an RDD containing all pairs of elements with matching keys in `this` and `other`. Each
   * pair of elements will be returned as a (k, (v1, v2)) tuple, where (k, v1) is in `this` and
   * (k, v2) is in `other`. Uses the given Partitioner to partition the output RDD.
   */
  def join[W](other: RDD[(K, W)], partitioner: Partitioner): RDD[(K, (V, W))] = self.withScope {
    
    
    this.cogroup(other, partitioner).flatMapValues( pair =>
      for (v <- pair._1.iterator; w <- pair._2.iterator) yield (v, w)
    )
  }

d. Ingrese el método cogroup, el núcleo es CoGroupedRDD, de acuerdo con dos necesitan unir RDD y un particionador. En la primera unión, ninguno de los rdd tiene un particionador, por lo que en este paso, los dos rdds deben realizar una reproducción aleatoria en función del particionador entrante, por lo que la primera unión es una dependencia amplia. La segunda combinación se ha dividido en zonas en este momento y no es necesario volver a mezclar. Entonces el segundo es dependencia estrecha

  /**
   * For each key k in `this` or `other`, return a resulting RDD that contains a tuple with the
   * list of values for that key in `this` as well as `other`.
   */
  def cogroup[W](other: RDD[(K, W)], partitioner: Partitioner)
      : RDD[(K, (Iterable[V], Iterable[W]))] = self.withScope {
    
    
    if (partitioner.isInstanceOf[HashPartitioner] && keyClass.isArray) {
    
    
      throw new SparkException("HashPartitioner cannot partition array keys.")
    }
    val cg = new CoGroupedRDD[K](Seq(self, other), partitioner)
    cg.mapValues {
    
     case Array(vs, w1s) =>
      (vs.asInstanceOf[Iterable[V]], w1s.asInstanceOf[Iterable[W]])
    }
  }

e. Ambos imprimen OneToOneDependency, porque en CoGroupedRDD, en el método getDependencies, si el rdd tiene un particionador, devolverá OneToOneDependency (rdd).

  override def getDependencies: Seq[Dependency[_]] = {
    
    
    rdds.map {
    
     rdd: RDD[_] =>
      if (rdd.partitioner == Some(part)) {
    
    
        logDebug("Adding one-to-one dependency with " + rdd)
        new OneToOneDependency(rdd)
      } else {
    
    
        logDebug("Adding shuffle dependency with " + rdd)
        new ShuffleDependency[K, Any, CoGroupCombiner](
          rdd.asInstanceOf[RDD[_ <: Product2[K, _]]], part, serializer)
      }
    }
  }
  1. ¿Cuándo se une una dependencia amplia y cuándo es una dependencia estrecha?
    Del análisis anterior se puede saber que si las dos tablas a unir ya tienen un particionador y el número de particiones es el mismo, la misma clave está en la misma partición en este momento. Es una dependencia estrecha. Por el contrario, si no hay particiones en las dos tablas que deban unirse o el número de particiones es diferente, y se requiere la reproducción aleatoria al unirse, entonces es una dependencia amplia

Supongo que te gusta

Origin blog.csdn.net/weixin_38813363/article/details/111868410
Recomendado
Clasificación