Spark 직렬화, 종속성, 지속성

직렬화

폐쇄 확인

직렬화 방법 및 속성

종속성 

RDD 혈액 관계

RDD 좁은 종속성

RDD 전체 종속성

RDD 업무 분과

RDD 지속성

RDD 캐시 캐시

RDD 체크포인트 체크포인트

캐시와 체크포인트의 차이점


직렬화

폐쇄 확인

        계산의 관점에서 연산자 이외의 코드는 Driver 측에서 실행되고 연산자 내부의 코드는 Executor 측에서 실행됩니다. 그러면 스칼라 함수형 프로그래밍에서 연산자 외부의 데이터는 종종 연산자에서 사용되므로 클로저 효과를 형성합니다.사용된 연산자 외부의 데이터를 직렬화할 수 없으면 데이터를 직렬화할 수 없다는 의미입니다. 실행을 위해 Executor 측에 값을 넘기므로 작업 계산을 수행하기 전에 클로저에 있는 객체를 직렬화할 수 있는지 여부를 확인해야 하는데 이 작업을 클로저 감지라고 합니다. Scala2.12 이후 클로저 컴파일 방법이 변경되었습니다.

직렬화 방법 및 속성

        계산의 관점에서 연산자 이외의 코드는 Driver 측에서 실행되고 연산자 내부의 코드는 Executor 측에서 실행됩니다.

object spark_02 {
  def main(args: Array[String]): Unit = {
    //准备环境
    //"*"代表线程的核数   应用程序名称"RDD"
    val sparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
    val sc = new SparkContext(sparkConf)
    val rdd: RDD[String] = sc.makeRDD(Array("hello world", "hello spark", "hive", "atguigu"))


    //创建查询对象
    val search = new Search("h")
    //函数传递,打印:ERROR Task not serializable
    search.getMatch1(rdd).collect().foreach(println)

    println("========================================")
    
    //属性传递,打印:ERROR Task not serializable
    search.getMatch2(rdd).collect().foreach(println)
    
    //关闭环境
    sc.stop()
  }
}

//查询对象
//类的构造参数是类的属性,构造参数需要进行闭包检查(对类进行闭包检查)
class Search(query:String) extends Serializable {
  def isMatch(s: String): Boolean = {
    s.contains(query)
  }
  // 函数序列化案例
  def getMatch1 (rdd: RDD[String]): RDD[String] = {
    rdd.filter(isMatch)
  }
  // 属性序列化案例
  def getMatch2(rdd: RDD[String]): RDD[String] = {
    rdd.filter(x => x.contains(query))
  }
}

종속성 

        두 개의 인접한 RDD 관계를 종속성이라고 합니다.

RDD 혈액 관계

        여러 연속 RDD의 종속 관계를 혈액 관계라고 합니다.

        RDD는 거친 변환, 즉 많은 수의 레코드에서 수행되는 단일 작업만 지원합니다. 손실된 파티션을 복원하기 위해 기록할 RDD의 일련의 Lineage(리니지)를 생성합니다. RDD의 Lineage는 RDD의 메타데이터 정보와 변환 동작을 기록하며, RDD의 일부 파티션 데이터가 손실되면 이 정보를 기반으로 손실된 데이터 파티션을 다시 계산하고 복원할 수 있습니다.

val fileRDD: RDD[String] = sc.textFile("input/1.txt")
println(fileRDD.toDebugString) //打印输出血缘关系
println("----------------------")
val wordRDD: RDD[String] = fileRDD.flatMap(_.split(" "))
println(wordRDD.toDebugString)
println("----------------------")
val mapRDD: RDD[(String, Int)] = wordRDD.map((_,1))
println(mapRDD.toDebugString)
println("----------------------")
val resultRDD: RDD[(String, Int)] = mapRDD.reduceByKey(_+_)
println(resultRDD.toDebugString)
resultRDD.collect()

RDD 좁은 종속성

        좁은 종속성은 각 부모(업스트림) RDD의 파티션이 자식(다운스트림) RDD의 최대 하나의 파티션에서 사용됨을 의미합니다. 좁은 종속성은 우리 이미지의 유일한 자식과 비교됩니다.

RDD 전체 종속성

        광범위한 의존성은 동일한 부모(업스트림) RDD의 파티션이 여러 자식(다운스트림) RDD의 파티션에 종속되어 셔플을 유발함을 의미합니다.요약: 광범위한 의존성의 은유는 다중 출생입니다.

RDD 업무 분과

        RDD 작업 세분화는 Application, Job, Stage 및 Task로 나뉩니다.

  • 응용 프로그램: SparkContext를 초기화하여 응용 프로그램을 생성합니다.
  • 작업: 작업 연산자는 작업을 생성합니다.
  • 단계: 단계는 ShuffleDependencies의 수에 1을 더한 것과 같습니다.
  • 작업: 단계에서 마지막 RDD의 파티션 수는 작업 수입니다.

참고: Application->Job->Stage->Task의 각 계층은 1:n 관계를 가집니다.

RDD 지속성

RDD 캐시 캐시

        RDD는 이전 계산 결과를 Cache 또는 Persist 방식으로 캐싱하며, 기본적으로 데이터는 JVM의 힙 메모리에 캐싱됩니다. 다만, 이 두 메소드를 호출한다고 바로 캐싱되지는 않고 후속 액션 연산자가 트리거되면 RDD는 컴퓨팅 노드의 메모리에 캐싱되었다가 나중에 재사용된다.

// cache 操作会增加血缘关系,不改变原有的血缘关系
println(wordToOneRdd.toDebugString)
// 数据缓存。
wordToOneRdd.cache()
// 可以更改存储级别
//mapRdd.persist(StorageLevel.MEMORY_AND_DISK_2)

        캐시가 손실되거나 메모리 부족으로 인해 메모리에 저장된 데이터가 삭제될 수 있습니다 RDD의 캐시 오류 허용 메커니즘은 캐시가 손실된 경우에도 계산이 올바르게 수행될 수 있도록 보장합니다. RDD를 기반으로 한 일련의 변환을 통해 손실된 데이터를 다시 계산하게 되는데, RDD의 각 파티션은 상대적으로 독립적이기 때문에 누락된 부분만 계산하면 되고 모든 파티션을 다시 계산할 필요는 없습니다. Spark는 일부 Shuffle 작업(예: reduceByKey)의 중간 데이터에 대해 영구 작업을 자동으로 수행합니다. 이것의 목적은 노드 셔플이 실패할 때 전체 입력을 다시 계산하지 않도록 하는 것입니다. 그러나 실제 사용에서 데이터를 재사용하려면 여전히 persist 또는 cache를 호출하는 것이 좋습니다.

RDD 체크포인트 체크포인트

        소위 체크포인트는 실제로 RDD의 중간 결과를 디스크에 기록하는 것입니다.긴 혈연 종속성으로 인해 내결함성 비용이 너무 높으므로 중간 단계에서 체크포인트 내결함성을 수행하는 것이 좋습니다. 체크포인트 이후의 노드에 문제가 있는 경우 체크포인트 시작부터 Bloodline을 다시 실행하여 오버헤드를 줄일 수 있습니다. RDD의 체크포인트 작업은 즉시 실행되지 않으며 이를 트리거하려면 Action 작업을 실행해야 합니다.

// 设置检查点路径
sc.setCheckpointDir("./checkpoint1")
// 创建一个 RDD,读取指定位置文件:hello atguigu atguigu
val lineRdd: RDD[String] = sc.textFile("input/1.txt")
// 业务逻辑
val wordRdd: RDD[String] = lineRdd.flatMap(line => line.split(" "))
val wordToOneRdd: RDD[(String, Long)] = wordRdd.map {
 word => {
 (word, System.currentTimeMillis())
 }
}
// 增加缓存,避免再重新跑一个 job 做 checkpoint
wordToOneRdd.cache()
// 数据检查点:针对 wordToOneRdd 做检查点计算
wordToOneRdd.checkpoint()
// 触发执行逻辑
wordToOneRdd.collect().foreach(println)

캐시와 체크포인트의 차이점

  1. 캐시 캐시는 혈액 의존성을 차단하지 않고 데이터만 저장합니다. 검문소 검문소는 혈액 의존성을 차단합니다.
  2. Cache에 의해 캐싱된 데이터는 일반적으로 디스크, 메모리 등에 저장되어 신뢰도가 낮다. 검사점 데이터는 일반적으로 HDFS와 같은 고신뢰성 내결함성 고가용성 파일 시스템에 저장됩니다.
  3. 체크포인트 작업이 캐시 캐시에서 데이터를 읽기만 하면 되도록 checkpoint()의 RDD에 캐시 캐시를 사용하는 것이 좋습니다. 그렇지 않으면 RDD를 처음부터 계산해야 합니다.

추천

출처blog.csdn.net/dafsq/article/details/129466546