spark 内核笔记

    
    执行启动命令 jar 、wordcount
    sparkSubmit:
        yarnclient ->submitApplication-> ResourceManage
        bin/java 在NodeManager 启动进程,ApplicationMaster 
        ApplicationMaster进程启动后,需要向ResourceManage注册ApplicationMaster
        
    ApplicationMaster:
        // userclass wordCount
        //主线程main创建driver线程, 等待driver线程创建sc
        //-》driver线程创建sc后 阻塞
        //-》主线程申请计算所需资源,创建完成后恢复driver
        //-》driver线程和主线程合二为一, driver线程 里的userClass.join()
         
        主线程和driver线程交互执行
        
    ApplicationMaster 向resourceManage 申请资源,返回可以运行的containers
    根据container 的位置在 对应的NodeManger 用命令方式启动container,每个container 启动一个excutor
    driver里的schedulerBacked 和 excutor 里的 Excutorbackend 互相通信,用于driver给excutor发送任务
    启动完excutor ,注册excutor
    
    创建计算对象,进行任务的划分和阶段划分,每一个任务由一个线程执行,这些线程在线程池里面
    
    
    
    
    
    一般任务数是核数的2-3倍
    一个行动算子 就是一个job
    driver 负责excutor之间的任务调度
    excutor 负责任务的执行,会存储rdd 和 广播变量
    
    
    driver 和excutor 通信是通过rpc,rpc是进程间通信协议
    netty 通信框架
        BIO:block IO
        NIO:non block IO,new IO
        netty  使用了AIO,异步非阻塞IO
    具体通信流程:
     driver的transportClinent 发送数据给 excutor 的transportSever 
        放入inbox,经过终端处理,放入outbox,然后outbox内部的transportClinent 
        连接driver 的transportSever 返回数据,inbox和outbox底层都是链表
    
    spark的任务调度:
        一个行动算子是一个job
        一个job根据shuffle 划分阶段
        task 是依据stage的最后一个rdd的分区数
        公平调度器,如果一个队列任务执行比例低,下次调度任务时,优先调度
    本地化调度:
        RDD的首选位置,计算发给哪个位置更好
        RDD存储级别:process_local,
                     node_local,
                     rack_local(机架本地化),
                     没有级别,
                     any
    
    spark shuffle:
        影响shuffle的点: 1 磁盘
                          2 缓冲区(参数配置),读取缓冲区默认48M,当task数量比较多,可能需要调小
                          3 数据量(先filter 或者reduceByKey 数据量少 ,shuffle性能高)
                          4 写文件的方式
    下游的多个task访问上游一个task产生的同一个溢写文件,可能冲突
    解决方案:溢写文件存多份,
              溢写文件数量和下游的task数量相等,
              每个task单独访问
    上述方案出现的问题:核数多了后,小文件过多
    优化方案:一个cpu 核的所有task共享溢写文件,
                溢写文件数量和下游的task数量相等
    上述方案当机器数多了后,仍然小文件过多
    解决方案:一个机器的一个核存一份溢写文件
             该溢写文件分数据文件和索引文件,
             其中数据文件是分区排序的
             
    
    serializedShuffle     
        解释:一个对象序列化后,放入内存,可以经过压缩,这样方便传输,也可以存放更多的对象
        1)支持重定位的序列化,为了防止内存碎片,kryo序列化支持,java序列化不支持
        2)分区数不能大于16777216
        3)不能有预聚合
        
    sortshuffle:
        reduceByKey 就是这种shuffle
        if (has预聚合){
            会有外部聚合器
            会对key排序,这样数据查找速度快
            只需要对map的value进行合并
            map的底层是k,v 连起来的数组,可以看作c++的结构体数组
            用数组的原因是key是排序好的,可以通过索引及二分查找法快速定位
            扩展:环形缓冲区溢写前是对索引进行排序,数据不动,
                  这样只需要对内存的部分数据进行排序,就可以对所有数据进行排序
                  
            溢写条件:
                数据量是32的倍数,并且数据量大于5M,此时会申请内存
                当预估大小大于申请后的内存,需要进行溢写
                
                当元素个数大于INT 的最大值,强制溢写
            最终需要对这些临时溢写文件进行归并排序(如果能忽略,可以提高性能)
            
            如果只有内存中有数据,那么只需要对内存数据排序,然后写文件
            如果有溢写文件,需要merge,将内存数据和溢写文件合并,
                最终合并成一个索引文件和一个排序好的数据文件
        }     
        else{
            存放分区,k,v
        }     
    bypass sortshuffle:
        忽略归并排序的shuffle
        条件:1) 不能有预聚合功能 ,有预聚合的算子
                reduceByKey,foldByKey,combineByKey,aggregateByKey
                groupBykey没有
              2)分区数量必须小于阈值,默认是200
    
    为什么能忽略归并排序:
        写分区数量个文件,然后直接按照分区顺序合并成一个文件,
        同时创建这个文件的索引文件
    注意溢写文件多的时候用bypass sortshuffle 才合适,因为当内存足够大时,
        有可能sortshuffle 不溢写文件,工作中需要把bypass 阈值设高一点
        默认200,可以设为500
    
                

 

spark 内存管理:
        spark的结果可以把中间的计算过程通过内存传递
        jvm 默认启动的时候是当前可用内存的1/64,最大是1/4
        堆外内存是从操作系统申请的,不受gc控制,由程序自己控制
        kafka 数据文件是0拷贝技术,索引文件是虚拟内存映射
        
        executor 内存分3部分,分别是storage(30%),
                                    execution(30%),
                                    other(40%),
                                    预留300M内存,防止内存溢出
        
            rdd,catche 保存在存储内存
            shuffle 需要使用执行内存
            元数据信息保存在other
            执行内存和存储内存有动态占用机制

 

posted @ 2021-07-23 21:18  wangheng1409  阅读(58)  评论(0编辑  收藏  举报