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
我的理解:-
包含每个块的地址,这样每个块都可以在单独的节点上并行执行?这个问题实际上比人们想象的要复杂得多
- 这是我对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操作的主要入口点是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注册任务:
- 另一方面,我们拥有区块管理者家族:
- 包括驱动程序在内的每个执行者都有一个BlockManager
- BlockManagerMaster在驱动程序上运行
- BlockManagerMasterEndpoint是可通过BlockManagerMaster访问的rpc端点
- BlockManagerMaster可通过SparkEnv服务访问
- 当要求执行者启动Task()时,它
- 这个问题很有趣,也不是那么简单!
在深入了解Spark的核心源代码(2.4x)之后,以下是我对您的问题的理解和回答建议: