Analyse du code source de Checkpoint

Checkpoint est une fonction relativement avancée fournie par Spark. Parfois, par exemple, notre application Spark est extrêmement complexe, puis, du RDD initial à la fin de l'application entière, il y a de nombreuses étapes, telles que plus de 20 opérations de transformation. De plus, l'ensemble de l'application prend beaucoup de temps à s'exécuter, par exemple, il faut généralement 1 à 5 heures pour s'exécuter.

Dans les cas ci-dessus, il est plus approprié d'utiliser la fonction de point de contrôle. Parce que, pour les applications Spark particulièrement complexes, il existe un risque élevé qu'un RDD doive être utilisé de manière répétée. En raison de la défaillance du nœud, bien qu'il ait persisté auparavant, cela entraîne toujours une perte de données. C'est-à-dire qu'en cas de panne, il n'y a pas de mécanisme de tolérance aux pannes, donc lorsque le RDD est utilisé dans l'opération de transformation suivante, on constate que les données sont perdues (CacheManager). À ce moment, s'il y a Il n'y a pas de traitement tolérant aux pannes, alors il se peut que vous deviez recalculer les données à nouveau.

En bref, au vu de la situation ci-dessus, la tolérance aux pannes de l'ensemble de l'application Spark est très mauvaise.

Par conséquent, en réponse au problème d'application Spark complexe mentionné ci-dessus (le problème de l'absence de mécanisme de tolérance aux pannes). Vous pouvez utiliser la fonction checkponit.

Que signifie la fonction de point de contrôle? Checkpoint signifie que pour une chaîne RDD complexe, si nous nous inquiétons de certains RDD clés qui seront utilisés à plusieurs reprises à l'avenir, la défaillance du nœud peut entraîner la perte de données persistantes, alors nous pouvons cibler le RDD Surtout démarrer le point de contrôle mécanisme permettant d'atteindre la tolérance aux pannes et la haute disponibilité.

Checkpoint, c'est-à-dire tout d'abord, appelez la méthode setCheckpointDir () de SparkContext pour définir un répertoire de système de fichiers à tolérance de pannes, tel que HDFS, puis appelez la méthode checkpoint () sur le RDD. Après cela, après le travail où se trouve le RDD, un travail séparé sera lancé pour écrire les données RDD vérifiées dans le système de fichiers précédemment défini pour des opérations persistantes à haute disponibilité et tolérantes aux pannes.

Donc, à ce moment, même lorsque le RDD est utilisé plus tard, ses données persistantes sont accidentellement perdues, mais ses données peuvent toujours être lues directement à partir de son fichier de point de contrôle sans recalcul. (CacheManager)

1. Comment faire un checkpoint?
SparkContext.setCheckpointDir ()
RDD.checkpoint ()
2. Analyse du principe du point de contrôle
3. La différence entre Checkpoint et la persistance: changements de lignage
4. RDD.iterator (): lire les données du point de contrôle
5. Donner le RDD à vérifier, persister en premier (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]]
  }

Je suppose que tu aimes

Origine blog.csdn.net/m0_46449152/article/details/109562062
conseillé
Classement