前段时间在部门内部做了一个 大数据组件 的知识分享,期间说到 hdfs 的文件多副本存储 和 Spark 的数据本地性时,一位同学提到“既然文件在集群上有多个副本,那么,Spark 计算时会不会有重复?” 我的第一反应是不会重复,但,原因呢?
后面特意去查了下 Spark 的源码 以及 HDFS 上的文件读写过程,简单记录下我的理解。
1. Spark 读取 HDFS 上的文件时,为什么计算结果不会重复?
Spark 在读取 HDFS 上的文件时,首先向 HDFS 发起一个读请求,这时,Spark 相当于 HDFS 的读取 Client ,HDFS 接到 Client 的读请求后,会向 NameNode 发起通读请求,NameNode 会在元数据库中查询这个文件的 block 信息 和 block 副本信息等元数据,随后根据 Client 所在的节点位置,以及文件的 block 和 block 副本位置,选择将离 Client 最近的 block 或 block 副本 位置返回给 Client
【这里只会返回一个 block 或 block 副本的位置给 Client】,Client 拿到这个位置信息开始进行读取。
如果文件有多个block ,读取过程基本是相同的。
所以,Spark 在读取 HDFS 上的文件时,是不会因为 block 存在多个副本导致计算结果重复的。
2. Spark 计算时,Executor 是什么时候申请的?
以前,这一块儿我的理解是 在spark-submit 脚本提交时就申请和启动 Executor 了,随后才开始跑具体业务逻辑代码。但这和 Spark 的数据本地性又对不上,Spark 的源码中,是将 task 分发到 Executor 中去执行的,但是 spark-submit 如果还没有执行到业务逻辑代码时,是不知道到底需要计算的数据存在哪里的,如果,在 执行 业务逻辑代码前就已经启动 Executor 了,Spark 又如果保证计算的数据本地行呢?
这里我的理解上或许有一个误区,那就是在启动 spark-submit 时,会启动 AM ,负责 Executor 的申请和业务逻辑代码的执行。只不过在这一阶段只记录了需要申请的 Executor 数量和资源等信息,没有触发真正的资源申请动作,在运行业务逻辑代码时,获取到了 需要读取数据的文件位置后,正式向 AM 申请和启动 Executor 。
# 以前理解的执行顺序
spark-submit -> 申请和启动 AM -> 申请和启动 Executor -> 启动 Driver -> 执行业务逻辑代码
# 现在理解的执行顺序
spark-submit -> 申请和启动 AM -> 伪申请 Executor -> 启动 Driver -> 执行业务逻辑代码 -> 根据代码中读取的数据位置启动 Executor
如果业务逻辑代码中读取了多次数据,有多个文件位置,我的理解是以第一次读取的文件位置来启动 Executor ,这也解释了为什么在 Spark 应用中,有时候会出现多种数据本地行级别的 task 【虽然有时候是由于资源短缺造成的】。
仅此记录下我的理解,欢迎大家来讨论学习。