Análisis de código fuente de punto de control

Checkpoint es una función relativamente avanzada proporcionada por Spark. A veces, por ejemplo, nuestra aplicación Spark es extremadamente compleja y luego, desde el RDD inicial hasta el final de la aplicación completa, hay muchos pasos, como más de 20 operaciones de transformación. Además, toda la aplicación tarda mucho en ejecutarse; por ejemplo, suele tardar entre 1 y 5 horas en ejecutarse.

En los casos anteriores, es más adecuado utilizar la función de punto de control. Porque, para aplicaciones Spark particularmente complejas, existe un alto riesgo de que haya un RDD que deba usarse repetidamente. Debido a la falla del nodo, aunque se ha persistido antes, aún conduce a la pérdida de datos. Es decir, cuando hay una falla, no existe un mecanismo tolerante a fallas, por lo que cuando se utiliza el RDD en la operación de transformación posterior, se encontrará que los datos están perdidos (CacheManager). no es un procesamiento tolerante a fallas, entonces puede que tenga que volver a calcular los datos.

En resumen, en vista de la situación anterior, la tolerancia a fallas de toda la aplicación Spark es muy pobre.

Por lo tanto, en respuesta al problema complejo de la aplicación Spark mencionado anteriormente (el problema del mecanismo sin tolerancia a fallas). Puede utilizar la función checkponit.

¿Qué significa la función de punto de control? Checkpoint significa que para una cadena RDD compleja, si estamos preocupados por algunos RDD clave que se usarán repetidamente en el futuro, la falla del nodo puede conducir a la pérdida de datos persistentes, entonces podemos apuntar al RDD Especialmente iniciar el punto de control. mecanismo para lograr tolerancia a fallas y alta disponibilidad.

Checkpoint, es decir, primero, llame al método setCheckpointDir () de SparkContext para establecer un directorio de sistema de archivos tolerante a fallas, como HDFS; luego, llame al método checkpoint () en el RDD. Después de eso, después del trabajo donde se encuentra el RDD, se iniciará un trabajo separado para escribir los datos del RDD verificados en el sistema de archivos establecido previamente para operaciones persistentes de alta disponibilidad y tolerantes a fallas.

Entonces, en este momento, incluso cuando el RDD se usa más tarde, sus datos persistentes se pierden accidentalmente, pero sus datos aún se pueden leer directamente desde su archivo de punto de control sin volver a calcularlos. (CacheManager)

1. ¿Cómo hacer checkpoint?
SparkContext.setCheckpointDir ()
RDD.checkpoint ()
2. Análisis del principio del punto de control
3. La diferencia entre el punto de control y la persistencia: cambios de linaje
4. RDD.iterator (): lee los datos del punto de control
5. Dale al RDD el punto de control, persiste primero (StorageLevel .DISK_ONLY)

RDD.iterator
  /**
   * 先persist,再checkpoint
   * 那么首先执行到该rdd的iterator之后,会发现storageLevel != StorageLevel.NONE
   * 就通过CacheManager去获取数据,此时发现通过BlockManager获取不到数据(因为第一次执行)
   * 那么就会第一次还是会计算一次该rdd的数据,然后通过CacheManager的putInBlockManager将其通过
   * BlockManager进行持久化
   * rdd所在的job运行结束了,然后启动单独job进行checkpoint操作,此时是不是又会执行到该rdd的iterator方法
   * 那么就会发现持久化级别不为空,默认从BlockManager直接读取持久化数据(正常情况下可以)但是问题是,如果非正常情况下
   * 持久化数据丢失了,那么此时会走else,调用computeOrReadCheckpoint方法判断如果rdd是isCheckpoint为ture
   * 就会用用它的父rdd的iterator方法,其实就是从checkpoint外部文件系统中读取数据
   */
  
  final def iterator(split: Partition, context: TaskContext): Iterator[T] = {
    
    
    // TODO getOrCompute  如果StorageLevel不为NONE,之前持久化过RDD,那么就不要直接去从父RDD执行算子,计算新的RDD的partition了
    // 优先尝试使用CacheManager,去获取持久化的数据
    if (storageLevel != StorageLevel.NONE) {
    
    
      SparkEnv.get.cacheManager.getOrCompute(this, split, context, storageLevel)
    } else {
    
    
      // TODO 进行rdd partition的计算
      computeOrReadCheckpoint(split, context)
    }
  }
=> computeOrReadCheckpoint
  private[spark] def computeOrReadCheckpoint(split: Partition, context: TaskContext): Iterator[T] =
  {
    
    
    // TODO   MapPartitionsRDD.compute
    if (isCheckpointed) firstParent[T].iterator(split, context) else compute(split, context)
  }
==> CheckpointRDD.compute
  override def compute(split: Partition, context: TaskContext): Iterator[T] = {
    
    
    val file = new Path(checkpointPath, CheckpointRDD.splitIdToFile(split.index))
    CheckpointRDD.readFromFile(file, broadcastedConf, context)
  }
===> CheckpointRDD.readFromFile  读取checkpoint数据
  def readFromFile[T](
      path: Path,
      broadcastedConf: Broadcast[SerializableWritable[Configuration]],
      context: TaskContext
    ): Iterator[T] = {
    
    
    val env = SparkEnv.get
    val fs = path.getFileSystem(broadcastedConf.value.value)
    val bufferSize = env.conf.getInt("spark.buffer.size", 65536)
    val fileInputStream = fs.open(path, bufferSize)
    val serializer = env.serializer.newInstance()
    val deserializeStream = serializer.deserializeStream(fileInputStream)

    // Register an on-task-completion callback to close the input stream.
    context.addTaskCompletionListener(context => deserializeStream.close())

    deserializeStream.asIterator.asInstanceOf[Iterator[T]]
  }

Supongo que te gusta

Origin blog.csdn.net/m0_46449152/article/details/109562062
Recomendado
Clasificación