Spark基础
RDD是只读记录分区的集合,只能通过在其他RDD执行确定的转换操作(如map、join和groupBy)或直接读取外部存储而创建,然而这些限制使得实现容错的开销很低。与分布式共享内存系统需要付出高昂代价的检查点和回滚机制不同,RDD通过Lineage来重建丢失的分区; 一个RDD包含如何从其他RDD衍生所必需的的相关信息,从而不需要检查点操作就可以重建丢失的数据分区。
用户程序从开始提交到最终的计算:
- 用户程序创建SparkContext,连接ClusterManager,ClusterManager会根据用户提交时设置的CPU和内存等信息为本次提交分配计算资源,启动Executor。
- Driver将用户程序划分为不同的执行阶段(stage),每个阶段由一组完全相同的Task组成,分别作用于待处理数据的不同分区,stage划分完成并创建Task后,Driver会向Executor发送Task。
- Executor接收到Task后,会下载任务运行所需要的依赖,开始执行Task,并将Task执行情况汇报给Diver。
- Driver根据收到的Task的运行状态来处理不同的状态更新,在所有Task都正确执行或者超过执行次数的限制仍然没有执行成功时停止。
对于RDD来说,每个并行分片都会被一个计算任务处理,并决定并行计算的粒度,用户可以在创建RDD时指定分片个数,如果不指定,则采用默认值,默认值就是程序分配到的CPU Core的数目
任务调度模块
任务调度模块主要包含两大部分:DAGScheduler和TaskScheduler,他们分别负责将用户提交的计算任务按照DAG划分为不同的阶段并且将不同阶段的计算任务提交到集群进行最终的计算
stage的划分是通过DAG Scheduler实现的,宽依赖是spark划分stage的依据。用户提交任务以后,DAG Scheduler从DAG末端开始往前回溯,窄依赖则划分到当前stage,宽依赖则划分为不同的stage。
DAG的最后一个阶段会为每个结果的partition生成一个resultTask,其余所有阶段都会生成shuffleMapTask,生成的task会被发送到已经启动的Executor上执行,resultTask的执行结果回传给driver,shuffleMapTask的结果进行缓存,作为下一个stage的输入。
任务的提交是通过TaskScheduler实现的,每个TaskScheduler都有一个SchedulerBackend,其中TaskScheduler负责Application的不同Job之间的调度,在Task执行失败时启动重试机制,并且为执行速度慢的Task启动备份任务,而SchedulerBackend负责与ClusterManager交互获取该Application分配到的资源,并传给TaskScheduler,最终由TaskScheduler给Task分配计算资源。
一组Task以TaskSet的形式发送给TaskScheduler以后,调度方式主要是FIFO(id小的优先调度)和Fair(根据配置文件构建调度树)两种
spark运行模式
local模式:master和worker都在本机
standalone:使用自身的资源管理器
yarn:用yarn的资源管理器
client模式:提交任务的节点就是driver节点,适合本地测试
cluster模式:提交任务的节点不一定是driver节点,driver节点是随机选举出来的,用于生产环境
集群容错处理
容灾系统在实现中可以分为两个层次:数据容灾和应用容灾。数据容灾指建立一个异地的数据系统,作为本地关键应用的一个备份;应用容灾指在数据容灾的基础上,在异地建立一套完整的和本地生产环境相同的备份应用系统,在灾难情况下,由远程系统迅速接管业务运行
Master的高可用:集群可以部署多个Master,元数据信息都存储在zk中,借助zk的选举机制选出一个Master作为集群的管理者,其他的作为备份,当Master异常退出,就从备份的Master中选出一个充当集群的管理者
Worker异常退出:Master会将该Worker上所有Executor状态标记为丢失,并通知AppClient,根据是否需要重启决定重启该Worker上运行的Driver Client或者删除
Executor异常退出:Master会为该Application分配新的Executor
Standalone模式的Executor分配
在Standalone模式下集群启动时,Worker会向Master注册,使Master可以感知整个集群进而对集群进行管理,Master通过zk简单实现高可用;客户端通过创建SparkContext完成Application的注册,由Master为其分配Executor,在应用方创建了RDD并在这个RDD上进行了很多转换后,触发Action,通过DAGScheduler将DAG划分为不同的Stage,并将Stage装换为TaskSet交给TaskScheduler,由TaskScheduler为Task分配最终的计算资源,启动Executor并执行Task。
Shuffle
Hash Based Shuffle
在Spark 1.0之前,Spark只支持Hash Based Shuffle,每个Shuffle Map Task根据key的hash值计算出每个key需要写入的partition,然后将数据单独写入一个文件,这个partition就对应了下游的一个shuffle map task或者result task,作为下游的输出。
这种方式的问题在于输出文件太多,有number(shuffle task) * num(following task),而同时打开多个文件和随机读都容易成为性能的瓶颈,因此后来spark加入了Shuffle Consolidate机制,即运行在同一个core的shuffle map task可以采用追加的方式写入文件,而不是新建一个文件,这样输出的文件数为number(cores) * num(following task),虽然文件有所减少,但仍然没有解决上面两个问题,因此spark2.0将默认的Hash Based Shuffle换成了Sort Based Shffule。
Sort Based Shuffle
Sort Based Shffule中每个shuffle map task不会为每个Reducer生成一个单独的文件,他会把所有的结果写到一个文件里,同时生成一个index文件,Reducer可以通过index文件取得他所需要处理的数据,避免产生大量的文件,节省内存的使用。shuffle map task会按照key对应的partition id进行排序,同一个partition内的key不会排序,这样可以减少负收益的排序造成的性能损耗.