Scala 不同的文件块如何在不同的节点上并行处理?

Scala 不同的文件块如何在不同的节点上并行处理?,scala,apache-spark,Scala,Apache Spark,考虑以下示例程序以供参考 val text = sc.textFile("file_from_local_system.txt");// or file can also be on hdfs val counts = text.flatMap(line => line.split(" ") ).map(word => (word,1)).reduceByKey(_+_) counts.collect 我的理解:- 驱动程序创建沿袭图(LG)/计算作业、阶段和任务 然后让集群管

考虑以下示例程序以供参考

val text = sc.textFile("file_from_local_system.txt");// or  file can also be on hdfs
val counts = text.flatMap(line => line.split(" ")
 ).map(word => (word,1)).reduceByKey(_+_) counts.collect
我的理解:-

  • 驱动程序创建沿袭图(LG)/计算作业、阶段和任务
  • 然后让集群管理器(比如spark standalone cluster manager)根据任务分配资源
  • 希望它是正确的

    问题:-

    我的问题在第一步。要计算可并行执行的任务数,驱动程序(DP)应 还要知道该文件在磁盘上存储的块数

    DP在构建LG和内部任务时是否知道这一点
    包含每个块的地址,这样每个块都可以在单独的节点上并行执行?

    这个问题实际上比人们想象的要复杂得多

    • 这是我对HDFS案例的理解,您提到的数据节点是工作节点。因此,我将S3和AZURE Blob存储、第二代等排除在讨论之外,也就是说,这一解释假设了数据局部性原则——这一原则随着云计算的发展正在变得过时,除非高性能成为主流

    • 答案还不包括重新分区和减少方面,这些方面也会影响事物以及纱线动态资源分配,因此它假设纱线是集群管理器

    下面是:

    资源分配

    • 在DAG物理创建之前(基于包含任务的阶段),这些任务由向纱线发出请求的驱动程序预先分配。例如,考虑spark submit上的参数
    • 因此,你的第二点并不完全正确
    • 根据加工模式,让我们假设纱线集群模式,您将获得大量资源分配。
      • 例如,如果您有一个由5个数据/工作节点组成的集群,其中包含20个CPU(40个核心),那么如果您只提交并使用默认值,您可能会得到一个Spark应用程序(用于N个操作),该应用程序总共分配了5 x 1个核心,每个数据/工作节点分配1个核心
    • 每个Spark作业通常完全保留获得的资源
    • Spark作业是Spark应用程序的一部分。Spark应用程序可以有N个通常按顺序运行的操作
    • 请注意,如果无法分配所有资源,作业仍可能启动
    (司机)执行

    • 假设您的文件可以有11个分区,例如,2个分区用于4个节点,1个分区用于第5个数据/工作节点
    • 然后,在Spark术语中,您使用sc.textfile指定的文件将使用Hadoop二进制文件进行处理,该二进制文件在每个文件块的任务基础上工作,这意味着驱动程序将在第一阶段发出任务-总共11个。第一阶段是reduce所需的洗牌之前。
      • 因此,驱动程序在每个阶段获取信息并发出大量任务,这些任务(通过管道)并设置为由该工作节点的core=Executor顺序执行
      • 每个工作节点/数据节点可以有更多的执行器,这意味着执行速度更快,从而提高吞吐量
    • 这表明我们可以浪费资源。对于较小的文件,每个数据/工作节点默认分配1个核心可能是浪费,或者在重新分区后导致数据倾斜。但这是供以后考虑的
    其他考虑

    • 可以限制每个应用程序的执行者数量,从而限制作业。如果选择足够低的数量,即小于集群中的节点数量,并且文件分布在所有节点上,则需要将数据从工作节点/数据节点传输到另一个这样的节点。顺便说一句,这不是洗牌
    • S3是AWS存储,数据与工作节点分离。这与计算弹性有关

      • 这个问题很有趣,也不是那么简单! 在深入了解Spark的核心源代码(2.4x)之后,以下是我对您的问题的理解和回答建议:

      • 一般知识:

        • 所有Spark操作的主要入口点是SparkContext
        • Dag调度程序从SparkContext中实例化
        • SparkContext有一个runJob方法,它本身通知Dag调度程序调用其runJob方法。它是为给定的RDD及其相应的分区调用的
        • Dag调度程序基于作为任务集提交的阶段构建执行图
        • 提示:Dag调度程序可以通过与BlockManagerMaster通信来检索BlockID的位置
        • Dag调度器还使用低级TaskScheduler,它保存任务id和执行器id之间的映射
        • 向TaskScheduler提交任务对应于为阶段生成任务集,然后调用TaskSetManager
        • 有趣的是:作业的依赖关系由DAG调度程序管理,数据位置由TaskScheduler管理
        • 任务是单个工作单元,每个工作单元发送到一台机器(执行器)。

      • 让我们看一看Task.run()

        • 它向BlockManager注册任务:
          SparkEnv.get.blockManager.registerTask(taskAttemptId)
        • 然后,它创建一个TaskContextImpl()作为上下文,并调用一个runTask(上下文)
        • ResultTask类和ShuffleMapTask类都重写此runTask()
        • 我们每个分区有一个ResultTask
        • 最后,将数据反序列化为rdd。

      • 另一方面,我们拥有区块管理者家族:

        • 包括驱动程序在内的每个执行者都有一个BlockManager
        • BlockManagerMaster在驱动程序上运行
        • BlockManagerMasterEndpoint是可通过BlockManagerMaster访问的rpc端点
        • BlockManagerMaster可通过SparkEnv服务访问
        • 当要求执行者启动Task()时,它