分布式计算平台Spark:Core(一)

分布式计算平台Spark:Core(一)

一、课程回顾

  1. Spark介绍

    • 什么是Spark?
      • 分布式计算框架
    • 功能与模块
      • SparkCore:离线批处理:代码进行开发
        • 类似于MapReduce
        • 工作中:在IDEA中写代码,打成jar包放在集群中调度运行
      • SparkSQL:交互式处理:SQL开发
        • 类似于Hive
        • 工作中:用于实现数据仓库中的分析
      • SparkStreaming & StructStreaming:流式计算
        • 实时业务场景下的计算
      • Spark MLlib:机器学习算法库
      • Spark Graphx:图计算
    • 应用场景
      • 数据处理:ETL、分层转换
      • 数据分析
      • 机器学习
    • 特点
        • 积极的使用内存来实现数据的处理
          • 空间小:如果内存不足,可以将数据放在磁盘
          • 易丢失:血脉依赖关系
        • Task运行的方式:线程
          • 线程也是运行在进程中
          • MapReduce以进程方式来运行Task:每个Task都需要重新申请资源
          • Spark中的运行进程Executor是不会直接运行一个Task就释放的,等待所有Task的运行完成
      • 好使
        • 开发接口非常丰富
        • SQL、Java、Scala、Python、R
      • 通用性
        • 功能模块非常全面
      • 可以随处运行
        • Spark支持各种数据源的读写:HDFS、HBASE、Kafka、Hive
        • Spark可以运行在 多种资源平台中
  2. Spark的部署模式

    • 本地模式:基于本地的环境,做测试开发
    • 集群模式:分布式模式
      • Standalone
      • YARN
      • Messos
      • K8s
  3. Spark程序的开发

    • 开发流程
      • 读取数据
        • 目标:将数据变成分布式的数据单元
      • 转换数据:分布式处理转换
      • 保存结果
    • 开发规则
      • SparkCore中会将所有数据读取放入一个抽象的集合中:RDD
        • RDD:弹性分布式数据集
          • 弹性:RDD基于内存来实现
            • 但是依旧通过不同的机制来保证数据存储和数据安全
          • 分布式:RDD中数据是分区存储在多台机器上分布式存储的
            • 分区存储的
            • 所有分区的数据加在一起才是一个完整的RDD的数据
          • 数据集:数据的集合
            • 类似于Scala中List集合
        • 问题:RDD怎么构建的,谁构建的?
          • 从HDFS等Hadoop文件系统中读取数据构建
          • 从一个Scala集合构建
          • RDD的构建:driver programe
        • driver programe
          • Spark的任何一个程序都必须包含driver 进程,在main方法中
          • 代码中:SparkContext
          • 功能:申请资源、解析代码、构建RDD、分配Task运行
      • 调用RDD的函数来实现数据的转换
        • 转换函数:基本函数与Scala中集合的函数基本一致
        • 并行操作体现在什么位置?
          • RDD的每个分区会执行一次这个函数
      • 保存结果:调用RDD的保存数据的函数来保存结果
  4. 排序实现

    • sortByKey

      image-20201217095552379

    • sortBy

      def sortBy[K](
            f: (T) => K,//指定按照谁进行排序
            ascending: Boolean = true,
            numPartitions: Int = this.partitions.length)
      

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IeuZVDXG-1610514088306)(Day39_分布式计算平台Spark:Core(一).assets/image-20201217095817230.png)]

    • top:小数据量的排序选取中是比较合适的

      def top(num: Int)(implicit ord: Ordering[T]): Array[T]
      

      image-20201217100346991

    package bigdata.itcast.cn.spark.scala.topkey
    
    import org.apache.spark.rdd.RDD
    import org.apache.spark.{SparkConf, SparkContext}
    
    /**
      * @ClassName SparkCoreWCTopKey
      * @Description TODO Spark Core实现词频统计,并且排序
      * @Date 2020/12/17 9:32
      * @Create By     Frank
      */
    object SparkCoreWCTopKey {
      def main(args: Array[String]): Unit = {
        /**
          * step1:初始化一个SparkContext
          */
        //构建配置对象
        val conf = new SparkConf()
          .setAppName(this.getClass.getSimpleName.stripSuffix("$"))
          .setMaster("local[2]")
        //构建SparkContext的实例,如果存在,直接获取,如果不存在,就构建
        val sc = SparkContext.getOrCreate(conf)
        //调整日志级别
        sc.setLogLevel("WARN")
    
    
        /**
          * step2:实现数据的处理过程:读取、转换、保存
          */
        //todo:1-读取
        val inputRdd: RDD[String] = sc.textFile("datas/wordcount/wordcount.data")
        println(s"first line = ${inputRdd.first()}")
        println(s"count = ${inputRdd.count()}")
        //todo:2-转换
        val rsRdd = inputRdd
            //对非法数据的过滤:def filter(f: T => Boolean)
            .filter(line => null != line && line.trim.length > 0)
            //提取所有单词,放到一个集合中
            .flatMap(line => line.trim.split("\\s+"))
            //转换为二元组
            .map(word => (word,1))
            //按照单词分组聚合
            .reduceByKey((tmp,item) => tmp+item)
            //方式一:sortByKey:def sortByKey(ascending: Boolean = true, numPartitions: Int = self.partitions.length),只能对二元组类型进行排序
            //只能对Key排序,所以交换位置
    //        .map(tuple => tuple.swap)
    //        .sortByKey(ascending = false)
            //方式二:sortBy
    //        .sortBy(tuple => -tuple._2)
    //        .take(3)
            //方式三:top:直接对数据进行排序,自动降序排序,取前N个值
            .top(3)(Ordering.by(tuple => tuple._2))
            .foreach(println)
        //todo:3-保存
    
    
    
        /**
          * step3:释放资源
          */
        Thread.sleep(1000000L)
        sc.stop()
    
    
      }
    }
    
    
  5. 反馈问题

    • spark的是数据存储结构是使用内存构建弹性分布式数据集RDD对数据进行运算和缓存,对弹性这个词不太理解
    • spark中间的结果是如何控制储存在内存而不是磁盘有点好奇
    • TASK以进程方式维护与以线程方式维护的区别
    • 对高阶函数的应用不是很熟悉,希望能有详细的注释
    • 课程时间希望调整为二三模式,加快授课进度

image-20201217105122748

二、课程目标

  1. 工作中如何运行Spark程序
    • 提交jar包运行自己开发的程序
    • Executor的个数和资源的分配
    • Spark on YARN
  2. deploymode:面试中容易问到
    • client
    • cluster
  3. RDD
    • 设计、本质、功能、特点
    • 使用
      • 创建
      • 分区
  4. RDD中的函数【算子】
    • 分类
    • 常见的算子

三、Standalone集群运行

1、工作场景

  • step1:在IDEA中开发代码,进行本地测试
  • step2:打成jar包,提交到spark集群运行
    • Standalone
    • YARN
    • deploy:cluster
  • step3:通过调度工具调度程序的运行

2、spark-submit

  • 先将程序打成jar包

    package bigdata.itcast.cn.spark.scala.wordcount
    
    import org.apache.spark.rdd.RDD
    import org.apache.spark.{SparkConf, SparkContext}
    
    /**
      * @ClassName SparkCoreWordCount
      * @Description TODO 自己开发代码来实现Wordcount
      * @Date 2020/12/16 17:22
      * @Create By     Frank
      */
    object SparkCoreWordCount {
      def main(args: Array[String]): Unit = {
        /**
          * step1:先构建SparkContext:初始化资源对象
          */
    
        //构建一个SparkConf:用于管理当前程序的所有配置
        val conf = new SparkConf()
          //给当前程序设置一个名字
          .setAppName(this.getClass.getSimpleName.stripSuffix("$"))
          //设置当前程序运行的模式
    //      .setMaster("local[2]")
        //构建一个SparkContext对象
        val sc = new SparkContext(conf)
        //调整日志级别
        sc.setLogLevel("WARN")
    
        /**
          * step2:处理数据
          */
        //todo:1-读取数据
        //使用args(0)作为输入路径
        val inputRdd: RDD[String] = sc.textFile(args(0))
        //todo:2-处理数据
        val rsRdd = inputRdd
            .filter(line => null != line && line.trim.length >0)
            .flatMap(line => line.trim.split("\\s+"))
            .map(word => word -> 1)
            .reduceByKey((tmp,item) => tmp+item)
    
        //todo:3-保存结果
        rsRdd.foreach(println)
        rsRdd.saveAsTextFile(args(1)+"-"+System.currentTimeMillis())
    
        /**
          * step3:释放资源
          */
    //    Thread.sleep(1000000L)
        sc.stop()
      }
    }
    
    

    image-20201217110159328

  • 上传到Linux上,并将jar包放到HDFS:方便在任何一台机器直接运行、

    hdfs dfs -mkdir /spark/apps
    hdfs dfs -put spark-chapter01_2.11-1.0.0.jar.jar /spark/apps/
    

    image-20201217110329834

  • 运行:spark-submit

    • 用法

      [root@node1 spark]# bin/spark-submit -h
      Usage: spark-submit [options] <app jar | python file | R file> [app arguments]
      Usage: spark-submit --kill [submission ID] --master [spark://...]
      Usage: spark-submit --status [submission ID] --master [spark://...]
      Usage: spark-submit run-example [options] example-class [example args]
      
      Options:
        --master MASTER_URL         spark://host:port, mesos://host:port, yarn,
                                    k8s://https://host:port, or local (Default: local[*]).
        --deploy-mode DEPLOY_MODE   Whether to launch the driver program locally ("client") or
                                    on one of the worker machines inside the cluster ("cluster")
                                    (Default: client).
        --class CLASS_NAME          Your application's main class (for Java / Scala apps).
        --name NAME                 A name of your application.
        --jars JARS                 Comma-separated list of jars to include on the driver
                                    and executor classpaths.
        --packages                  Comma-separated list of maven coordinates of jars to include
                                    on the driver and executor classpaths. Will search the local
                                    maven repo, then maven central and any additional remote
                                    repositories given by --repositories. The format for the
                                    coordinates should be groupId:artifactId:version.
        --exclude-packages          Comma-separated list of groupId:artifactId, to exclude while
                                    resolving the dependencies provided in --packages to avoid
                                    dependency conflicts.
        --repositories              Comma-separated list of additional remote repositories to
                                    search for the maven coordinates given with --packages.
        --py-files PY_FILES         Comma-separated list of .zip, .egg, or .py files to place
                                    on the PYTHONPATH for Python apps.
        --files FILES               Comma-separated list of files to be placed in the working
                                    directory of each executor. File paths of these files
                                    in executors can be accessed via SparkFiles.get(fileName).
      
        --conf PROP=VALUE           Arbitrary Spark configuration property.
        --properties-file FILE      Path to a file from which to load extra properties. If not
                                    specified, this will look for conf/spark-defaults.conf.
      
        --driver-memory MEM         Memory for driver (e.g. 1000M, 2G) (Default: 1024M).
        --driver-java-options       Extra Java options to pass to the driver.
        --driver-library-path       Extra library path entries to pass to the driver.
        --driver-class-path         Extra class path entries to pass to the driver. Note that
                                    jars added with --jars are automatically included in the
                                    classpath.
      
        --executor-memory MEM       Memory per executor (e.g. 1000M, 2G) (Default: 1G).
      
        --proxy-user NAME           User to impersonate when submitting the application.
                                    This argument does not work with --principal / --keytab.
      
        --help, -h                  Show this help message and exit.
        --verbose, -v               Print additional debug output.
        --version,                  Print the version of current Spark.
      
       Cluster deploy mode only:
        --driver-cores NUM          Number of cores used by the driver, only in cluster mode
                                    (Default: 1).
      
       Spark standalone or Mesos with cluster deploy mode only:
        --supervise                 If given, restarts the driver on failure.
        --kill SUBMISSION_ID        If given, kills the driver specified.
        --status SUBMISSION_ID      If given, requests the status of the driver specified.
      
       Spark standalone and Mesos only:
        --total-executor-cores NUM  Total cores for all executors.
      
       Spark standalone and YARN only:
        --executor-cores NUM        Number of cores per executor. (Default: 1 in YARN mode,
                                    or all available cores on the worker in standalone mode)
      
       YARN-only:
        --queue QUEUE_NAME          The YARN queue to submit to (Default: "default").
        --num-executors NUM         Number of executors to launch (Default: 2).
                                    If dynamic allocation is enabled, the initial number of
                                    executors will be at least NUM.
        --archives ARCHIVES         Comma separated list of archives to be extracted into the
                                    working directory of each executor.
        --principal PRINCIPAL       Principal to be used to login to KDC, while running on
                                    secure HDFS.
        --keytab KEYTAB             The full path to the file that contains the keytab for the
                                    principal specified above. This keytab will be copied to
                                    the node running the Application Master via the Secure
                                    Distributed Cache, for renewing the login tickets and the
                                    delegation tokens periodically.
      
    • 常用的选项

      • 提交的选项

        --master:用于指定提交的模式,local、Standalone、yarn、messos、k8s
        	local[2]
        	spark://host:7077
        	yarn
        

        ​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xCPsTqOE-1610514088312)(Day39_分布式计算平台Spark:Core(一).assets/image-20201217110811823.png)]

        --deploy-mode:指定deploy模式,client、cluster两种模式
        --class:指定运行jar包中的哪个类
        --jars:用于指定添加需要的一些额外的包
        --conf:临时的修改Spark的属性配置
        
      • Driver进程资源的配置

        --driver-memory MEM:driver进程的内存资源
        
      • Executor进程的资源配置

        • 通用属性

          --executor-memory:每个Executor能够使用的内存(Default: 1G).
          
        • Spark standalone and Mesos

          --total-executor-cores:所有Executor总共使用的CPU核数
          
        • Spark standalone and YARN

          --executor-cores:指定每个Executor能用几核CPU
          
        • YARN-only

          --num-executors:指定Executor的个数
          
        • 问题:Standalone集群中,如何指定Executor的个数

          • 个数 = --total-executor-cores / --executor-cores

3、本地模式提交

SPARK_HOME=/export/server/spark
$SPARK_HOME/bin/spark-submit \
--master local[2] \
--class bigdata.itcast.cn.spark.scala.wordcount.SparkCoreWordCount \
hdfs://node1:8020/spark/apps/spark-chapter01_2.11-1.0.0.jar.jar \
/datas/wordcount.data \
/datas/output

4、Standalone集群提交

  • 直接提交

    SPARK_HOME=/export/server/spark
    $SPARK_HOME/bin/spark-submit \
    --master spark://node1:7077 \
    --class bigdata.itcast.cn.spark.scala.wordcount.SparkCoreWordCount \
    hdfs://node1:8020/spark/apps/spark-chapter01_2.11-1.0.0.jar.jar \
    /datas/wordcount.data \
    /datas/output
    

    image-20201217113431762

  • 调整Executor和Driver的资源

    SPARK_HOME=/export/server/spark
    $SPARK_HOME/bin/spark-submit \
    --master spark://node1:7077 \
    --driver-memory 512M \
    --executor-memory 512M \
    --executor-cores 1 \
    --total-executor-cores 2 \
    --class bigdata.itcast.cn.spark.scala.wordcount.SparkCoreWordCount \
    hdfs://node1:8020/spark/apps/spark-chapter01_2.11-1.0.0.jar.jar \
    /datas/wordcount.data \
    /datas/output
    

    image-20201217114030881

四、Spark on YARN

1、应用场景

  • 公司的硬件资源有限,Spark集群和Hadoop集群共用同一套硬件:所有资源也是共用的
    • 一台从节点:DataNode、NodeManger、Hadoop程序、Spark程序
  • 统一化资源管理平台:将MapReduce、Spark的程序都运行在YARN中
    • 主:ResourceManger
      • 将Spark的程序提交给YARN
    • 从:NodeManger
      • Executor启动在NodeManger中
    • 这里不需要再启动Master和Worker

2、集群配置

  • 修改yarn-site.xml

    <property>
    	    <name>yarn.log-aggregation-enable</name>
    	    <value>true</value>
    </property>
    <property>
    	    <name>yarn.log-aggregation.retain-seconds</name>
    	    <value>604800</value>
    </property>
    <property>
            <name>yarn.log.server.url</name>
            <value>http://node1:19888/jobhistory/logs</value>
    </property>
    <property>
    	    <name>yarn.nodemanager.pmem-check-enabled</name>
    	    <value>false</value>
    </property>
    <property>
    	    <name>yarn.nodemanager.vmem-check-enabled</name>
    	    <value>false</value>
    </property>
    
  • 分发

    cd /export/server/hadoop/etc/hadoop
    scp -r yarn-site.xml root@node2:$PWD
    scp -r yarn-site.xml root@node3:$PWD
    
  • 关闭Spark集群

    cd /export/server/spark
    sbin/stop-master.sh
    sbin/stop-slaves.sh
    sbin/stop-history-server.sh
    
  • 修改spark-env.sh

    #添加yarn的地址
    YARN_CONF_DIR=/export/server/hadoop/etc/hadoop
    
  • 配置HDFS上Spark jar包的存储位置:解决YARN运行Spark没有Spark的依赖包

    hdfs dfs -mkdir -p /spark/apps/jars/
    hdfs dfs -put /export/server/spark/jars/* /spark/apps/jars/
    
  • 修改spark-defaults.conf

    #为了在8088中能直接访问Spark程序的监控,所以这里这里做了转接,如果在yarn中点击history,就转接18080
    spark.yarn.historyServer.address   node1:18080
    #指定yarn运行时的spark的jar包的地址
    spark.yarn.jars  hdfs://node1:8020/spark/apps/jars/*
    
  • 分发

    cd /export/server/spark/conf/
    scp spark-env.sh spark-defaults.conf node2:$PWD
    scp spark-env.sh spark-defaults.conf node3:$PWD
    

3、启动测试

  • 启动YARN

    start-yarn.sh
    
  • 启动Jobhistoryserver

    mr-jobhistory-daemon.sh start historyserver
    
  • 启动Spark的HistoryServer

    /export/server/spark/sbin/start-history-server.sh 
    
  • 提交程序

    SPARK_HOME=/export/server/spark
    $SPARK_HOME/bin/spark-submit \
    --master yarn \
    --driver-memory 512M \
    --executor-memory 512M \
    --executor-cores 1 \
    --num-executors 3 \
    --class bigdata.itcast.cn.spark.scala.wordcount.SparkCoreWordCount \
    hdfs://node1:8020/spark/apps/spark-chapter01_2.11-1.0.0.jar.jar \
    /datas/wordcount.data \
    /datas/output
    

五、DeployMode

1、问题与功能

  • 问题:每次提交一个新的Spark程序

    • driver:每次driver进程都启动在提交程序的客户端机器上
    • executor:个数和资源由用户自己指定,分配在哪些从节点,由集群自动分配管理
    • 如果driver都启动在一台机器,会导致两个问题
      • 这台机器的负载会很高
      • 如果这台机器故障,会导致driver进程失效,影响所有程序的运行
  • driver功能:申请资源、解析调度、分配、监控

    • 本质:代码中就是main方法中的SparkContext,初始化构建这个对象,Driver进程就启动了
    • step1:像Master/ResourceManager提交资源的申请,让Master或者RM启动Executor
      • 启动几个
      • 每个Executor能使用多少资源
    • step2:所有的Executor进程都要向Driver进行反向注册,汇报自己的状态,等待Driver分配Task执行
    • step3:Driver进程开始解析代码,构建DAG,将DAG中的stage转换为Task、
    • step4:将所有Stage的Task分配给Executor运行、
    • step5:监控所有Task的运行
  • 解决:deploy模式:决定driver进程启动在哪台机器

    --deploy-mode DEPLOY_MODE   Whether to launch the driver program locally ("client") or
                                  on one of the worker machines inside the cluster ("cluster")
                                  (Default: client).
    
    • client
    • cluster

2、client

  • 默认的模式,一般用于测试环境
  • driver进程启动在客户端这台机器

3、cluster

  • 工作中一般生产时肯定选用cluster模式
  • Driver会随机分配在某台从节点中启动

4、区别

  • Driver运行的位置不同

  • client模式

    SPARK_HOME=/export/server/spark
    $SPARK_HOME/bin/spark-submit \
    --master spark://node1:7077 \
    --driver-memory 512M \
    --executor-memory 512M \
    --executor-cores 1 \
    --total-executor-cores 2 \
    --class bigdata.itcast.cn.spark.scala.wordcount.SparkCoreWordCount \
    hdfs://node1:8020/spark/apps/spark-chapter01_2.11-1.0.0.jar.jar \
    /datas/wordcount.data \
    /datas/output
    
    
    SPARK_HOME=/export/server/spark
    $SPARK_HOME/bin/spark-submit \
    --master spark://node1:7077 \
    --deploy-mode client \
    --driver-memory 512M \
    --executor-memory 512M \
    --executor-cores 1 \
    --total-executor-cores 2 \
    --class bigdata.itcast.cn.spark.scala.wordcount.SparkCoreWordCount \
    hdfs://node1:8020/spark/apps/spark-chapter01_2.11-1.0.0.jar.jar \
    /datas/wordcount.data \
    /datas/output
    

    image-20201217143741344

  • cluster

    SPARK_HOME=/export/server/spark
    $SPARK_HOME/bin/spark-submit \
    --master spark://node1:7077 \
    --deploy-mode cluster \
    --driver-memory 512M \
    --executor-memory 512M \
    --executor-cores 1 \
    --total-executor-cores 2 \
    --class bigdata.itcast.cn.spark.scala.wordcount.SparkCoreWordCount \
    hdfs://node1:8020/spark/apps/spark-chapter01_2.11-1.0.0.jar.jar \
    /datas/wordcount.data \
    /datas/output
    

    image-20201217143951923

    image-20201217144011632

5、Spark on YARN上的区别

  • MapReduce程序在YARN上运行的过程

    image-20201217144728416

    • AppMaster:每一个YARN运行的程序都会启动一个APPmaster进程来管理这个程序的运行
  • Spark on YARN client:driver进程启动在客户端

    image-20201217145112553

    • Driver和APPMaster都存在,是两个不同的进程

      • Driver运行在客户端机器
        • 解析、注册、调度分配
      • APPMaster运行在某一台NodeManger中
        • 资源申请,Executor启动监控
    • step1:Spark的driver向RM提交程序,申请启动APPMaster

    • step2:APPMaster会根据提交的请求向RM申请资源启动Executor

    • step3:所有的Executor反向注册到Driver进程中

    • step4:Driver解析和分配Task到Executor中运行

  • Spark on YARN cluster:driver进程启动在NodeManger中

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qx57uAlc-1610514088317)(Day39_分布式计算平台Spark:Core(一).assets/image-20201217145900234.png)]

    • Driver和APPMaster合并了在同一个进程中

      • Driver运行APPMaster进程中

6、main函数执行过程

六、数据抽象RDD

1、功能

  • 定义:弹性分布式数据集(Resilient Distributed Datasets (RDDs))

    • 弹性:RDD的所有数据、转换过程中的数据都存储在内存中实现

      • 内存空间如果不足,利用磁盘实现存储

      • 可以将RDD的数据缓存在内存中,如果下一次又用到了这个RDD的数据,不用重新构建,直接从缓存中读取

      • 如果内存不足:将数据缓存在磁盘中

      • 如果担心缓存的数据丢失,避免重复构建整个RDD的数据,可以将RDD数据存储在HDFS

      • 问题

        //第一次调用rsRDD的数据,触发了程序的运行,于是Task运行得到结果并打印数据,Task结束,rsRDD在内存中的数据也消失
        rsRdd.foreach(println)
        //第二次又调用了rsRDD
        rsRdd.saveAsTextFile(args(1)+"-"+System.currentTimeMillis())
        

        image-20201217151548591

    • 分布式:一个RDD中可以有多个分区:类似于MapReduce中split

      • 子RDD的分区个数 = 父RDD分区个数
      • 每个分区由一个Task进行处理转换,一个Task需要1CoreCPU来实现
    • 数据集:类似于一个集合,将各种类型的数据存储在RDD中

Spark revolves around the concept of a resilient distributed dataset (RDD), which is a fault-tolerant collection of elements that can be operated on in parallel. There are two ways to create RDDs: parallelizing an existing collection 

in your driver program, or referencing a dataset in an external storage system, such as a shared filesystem, HDFS, HBase, or any data source offering a Hadoop InputFormat.
  • fault-tolerant:高容错的集合
    • 血脉:每一个RDD都会记录自己这个RDD是怎么来的
      • 任何一个分区数据的丢失,都可以通过血脉进行恢复
    • 缓存机制
    • 持久化机制
  • operated on in parallel:可以被并行的操作
    • RDD是分布式的存储,一个RDD包含多个分区
    • 每个分区都可以并行进行处理转换

2、设计

  • Spark最终目的:构建分布式计算:多线程的并行计算
  • |
  • 将数据变成分布式数据:RDD
  • |
  • 通过每个线程运行每个Task对RDD的每个分区进行处理,实现并行化的分布式计算

3、特性

 * Internally, each RDD is characterized by five main properties:
 *
 *  - A list of partitions
 *  - A function for computing each split
 *  - A list of dependencies on other RDDs
 *  - Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)
 *  - Optionally, a list of preferred locations to compute each split on (e.g. block locations for
 *    an HDFS file)
  • 每一个RDD都由一系列的分区构建而成
  • 函数进行计算处理时会对每个分区进行并行计算处理
  • 每个RDD都会记录着与别的RDD之间的依赖关系【宽依赖,窄依赖】
    • 父RDD
    • 子RDD
  • 可选的,二元组类型的RDD可以自定义指定分区器
    • 默认分区器
      • HashPartition:根据Key的hash分区
      • RangeParition:范围分区
  • 可选的,Spark会自动计算每个Task运行的最优路径解
    • 就是MapReduce中本地优先计算、

image-20201217155949012

image-20201217160125303

4、创建

  • 方式一:parallelizing an existing collection

    • 并行化一个集合
        val seqData: Seq[Int] = Seq(1,2,3,4,5,6,7,8,9,10)
        val seqRDD: RDD[Int] = sc.parallelize(seqData)
        //打印分区的个数
        println(s"分区的个数为:${seqRDD.getNumPartitions}")
        //todo:2-转换数据
        //todo:3-输出结果
        seqRDD.foreach(num => {
          //打印分区编号和当前的数值
          println(s"分区=${TaskContext.getPartitionId()}      num=${num}")
        })
    
  • 方式二:读取外部文件系统:HDFS/MySQL/Hbase

    • 常规的文件读取:sc.textFile

      • hadoopFile:调用了Hadoop的TextInputFormat这个类实现对HDFS文件的读取
        • 返回值:HadoopRDD[(LongWritable,Text)]
      • .map(pair => pair._2.toString)
        • 将HadoopRDD中的Value【每一行的内容】取出
    • 小文件的读取方式:sc.wholeTextFiles

      • 将每一个文件作为RDD的一个元素,返回一个二元素类型的RDD,Key是文件名称,Value是文件内容
          val inputRDD = sc
            //读取所有文件,将每个文件构建成RDD集合的一个元素
            .wholeTextFiles("datas/ratings100/", minPartitions = 2)
            //将每个文件每一行取出来
            .flatMap(tuple => tuple._2.split("\\n"))
            println(s"Partitions Number = ${inputRDD.getNumPartitions}")
            println(s"Count = ${inputRDD.count()}")
      

5、分区个数

  • 查看分区个数

    • getNumPartitions:获取分区的个数
  • 默认的分区个数

    • HDFS文件:一个Block块对应RDD的一个分区
    • HBASE表:一个region对应RDD的一个分区
    • Kafka的Topic:一个Topic的分区对应RDD的分区
  • 设置分区个数:规则:尽量让RDD的分区个数 = 集群中所有Executor的CPU核数的2 ~ 3

    • 可以适当调整RDD的分区个数

    • 如何设置

      	1)、启动的时候指定的CPU核数确定了一个参数值:
      	spark.default.parallelism=指定的CPU核数(集群模式最小2)
      	2)、对于Scala集合调用parallelize(集合,分区数)方法
      	如果没有指定分区数,就使用spark.default.parallelism
      	如果指定了就使用指定的分区数(不要指定大于spark.default.parallelism)
      	3)、对于textFile(文件, 分区数)
      	defaultMinPartitions
      	如果没有指定分区数sc.defaultMinPartitions=min(defaultParallelism,2) 
      	如果指定了就使用指定的分区数sc.defaultMinPartitions=指定的分区数rdd的分区数
      	rdd的分区数
      	对于本地文件
      	rdd的分区数 = max(本地file的分片数, 	sc.defaultMinPartitions)
      	对于HDFS文件
      	rdd的分区数 = max(hdfs文件的block数目, sc.defaultMinPartitions)
      	所以如果分配的核数为多个,且从文件中读取数据创建RDD,即使hdfs文件只有1个切片,最后的Spark的RDD的partition数也有可能是2
      

七、RDD函数分类

1、问题与设计

image-20201217165933638

  • 程序中对于数据的处理有4句代码
    • 读取数据
    • 转换数据
    • 打印数据
    • 保存数据
  • 为什么有的代码会触发运行,有的代码不触发运行?
  • 由什么决定?
    • 如果只定义处理逻辑,而不实际的使用数据,不会触发job的运行的
    • 只有当你需要用到RDD中的数据的时候,才会触发job的运行
    • 设计原因
      • 定义逻辑并没有用实际的数据,这个数据在未来可能不被使用,构建数据是没有意义
      • 只有当这个数据被使用的时候,再根据逻辑关系,构建这个数据
  • 函数分类
    • 转换函数:Transform
      • 不触发job,只定义转换逻辑
    • 触发函数:Action
      • 触发job运行

2、Transform转换算子

image-20201217170558144

  • 特征

    • 不触发job运行,只定义转换逻辑
    • 返回值是一个新的RDD
  • 常用

    • filter

       def filter(f: T => Boolean): RDD[T]
      
    • map

      def map[U: ClassTag](f: T => U): RDD[U]
      
    • flatMap

      def flatMap[U: ClassTag](f: T => TraversableOnce[U]): RDD[U]
      
    • reduceByKey

      def reduceByKey(func: (V, V) => V): RDD[(K, V)]
      
    • sortByKey

      def sortByKey(ascending: Boolean = true, numPartitions: Int = self.partitions.length): RDD[(K, V)]
      

3、Action触发算子

image-20201217171101864

  • 特征

    • 会触发job的运行
    • 一般返回值不是RDD,一般为普通类型或者为空
  • 常用

    • foreach

      def foreach(f: T => Unit): Unit
      
    • saveAsTextFile

      def saveAsTextFile(path: String): Unit
      
    • count:统计元素的个数

    • first:返回RDD第一个元素的值

    • take(N):取RDD中的N个值,放入一个数组进行返回

    • top(N):排序取前N个值,放入一个数组进行返回

    • collect:将RDD的所有元素,放入一个数组返回

4、基础函数

  • map
  • flatMap
  • filter
  • foreach
  • saveAsTextFile

附录一:Spark Maven依赖

    <!-- 指定仓库位置,依次为aliyun、cloudera和jboss仓库 -->
    <repositories>
        <repository>
            <id>aliyun</id>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
        </repository>
        <repository>
            <id>cloudera</id>
            <url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
        </repository>
        <repository>
            <id>jboss</id>
            <url>http://repository.jboss.com/nexus/content/groups/public</url>
        </repository>
    </repositories>

    <properties>
        <scala.version>2.11.12</scala.version>
        <scala.binary.version>2.11</scala.binary.version>
        <spark.version>2.4.5</spark.version>
        <hadoop.version>2.6.0-cdh5.16.2</hadoop.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>${scala.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-core_${scala.binary.version}</artifactId>
            <version>${spark.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>${hadoop.version}</version>
        </dependency>
    </dependencies>

    <build>
        <outputDirectory>target/classes</outputDirectory>
        <testOutputDirectory>target/test-classes</testOutputDirectory>
        <resources>
            <resource>
                <directory>${project.basedir}/src/main/resources</directory>
            </resource>
        </resources>
        <!-- Maven 编译的插件 -->
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>net.alchim31.maven</groupId>
                <artifactId>scala-maven-plugin</artifactId>
                <version>3.2.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

章鱼哥TuNan&Z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值