• Spork: Pig on Spark实现分析


    介绍

    Spork是Pig on Spark的highly experimental版本号,依赖的版本号也比較久,如之前文章里所说。眼下我把Spork维护在自己的github上:flare-spork

    本文分析的是Spork的实现方式和详细内容。

    Spark Launcher

    在hadoop executionengine包路径下,写了一个Spark启动器,同MapReduceLauncher类似,会在launchPig的时候,把传入的物理运行计划进行翻译。
    MR启动器翻译的是MR的操作,以及进一步的MR JobControl。而Spark启动器将物理运行计划的部分物理操作直接翻译成了RDD的操作。
    有一个缺点是翻译成RDD算子之后,缺少优化过程,也就是直接物理操作的映射翻译。详细运行逻辑会全然交给Spark DAGScheduler去切分,由TaskScheduler去调度任务。
    比方对Pig来说。直到见到Dump/Store,才会触发整个翻译和launch。

    那么在这一次物理运行计划中,相应到Spark可能是多次任务。


    在眼下的实现方式下,翻译物理操作交给多个Convertor的实现类来完毕,
    public interface POConverter<IN, OUT, T extends PhysicalOperator> {
    	
        RDD<OUT> convert(List<RDD<IN>> rdd, T physicalOperator) throws IOException;
        
    }
    
    抽象类POConvertor提供了convert方法,输入參数中的List<RDD>是本次物理操作的前驱们产生的RDDs,能够觉得是会依赖的父RDDs。

    这样一次转化结果就是产生nextRDD。而nextRDD是否在spark上真正触发计算,眼下来看是不去控制的。也就是上面提到的,一次Pig物理运行计划可能会有Spark运行多次任务。


    在使用的时候。以-x spark的方式就能够启动以Spark为backend engine的Pig环境。

    以下详细看眼下做了哪些PO操作的转化工作。详细怎么转化的。

    Load/Store

    走的都是NewHadoopRDD路线。

    Load方面是通过POLoad获得文件路径,pigContext获得必要配置信息,然后交由SparkContext调用newAPIHadoopFile来获得NewHadoopRDD,最后把Tuple2<Text, Tuple>的RDD map成仅仅剩value的RDD<Tuple>。

    Store方面是先把近期的前驱rdd转会成Key为空Text的Tuple2<Text, Tuple>。然后映射为PairRDDFunctions。借助pigContext生成POStore操作,最后调用RDD的saveAsNewAPIHadoopFile存到HDFS上。


    Foreach、Filter、Limit

    ForEach里实现一个Iterator[T] => Iterator[T]的方法,把foreach转化为rdd.mapPartitions()方法。

    Iterator[T]=> Iterator[T]方法的实现。会依赖原本的POForEach来获得nextTuple和进行一些别的操作,来实现一个新的Iterator。

    对于hadoop backend的executionengine里的抽象类PhysicalOperator来说。

    setInput()和attachInput()方法是放入带处理的tuple数据。

    getNextTuple()的时候触发processTuple()。处理对象就是内部的Input Tuple。

    所以ForEach操作实现Iterator的时候。在readNext()方法里掺入了以上设置Input数据的操作,在返回前调用getNextTuple()返回处理后的结果。

     

    POFilter也是通过setInput()和attachInput()以及getNextTuple()来返回处理结果。

    所以在实现为RDD操作的时候。把以上步骤包装成一个FilterFunction,传入rdd.filter(Function)处理。

    POLimit同POFilter是全然一样的。


    Distinct

    如今RDD已经直接具备distinct(numPartitions: Int)方法了。


    这里的distinct实现同rdd里的distinct逻辑是全然一样的。

    第一步:把类型为Tuple的rdd映射成为Tuple2<Tuple, Object>。当中value部分是null的;

    第二步:进行rdd.reduceByKey(merge_function, parallelism)操作,merge_function对两个value部分的Object不做不论什么处理。也就是按key reduce且不正确value部分处理;

    第三步:对第二步的结果进行rdd.map(function, ClassTag)处理,function为得到Tuple2<Tuple, Object>里的._1,即key值:Tuple。



    Union

    Union是一次求并过程,直接new UnionRDD<Tuple>返回。

    因为UnionRDD处理的是Seq<RDD>。所以使用JavaConversions.asScalaBuffer(List<RDD<Tuple>>)进行一下转换再传入。



    Sort

    Sort过程:

    第一步:把Tuple类型的RDD转成Tuple2<Tuple, Object>类型。Object为空

    第二步:依据第一步结果。new OrderedRDDFunctions<Tuple, Object,Tuple2<Tuple, Object>>

    ,其sortByKey方法产出一个排过序的RDD<Tuple2<Tuple, Object>>。OrderedRDDFunctions里的Key类型必须是可排序的,比較器复用的是POSort的mComparator。sortByKey结果返回的是ShuffleRDD。其Partitioner是RangePartitioner,排序之后,每一个Partition里存放的都是一个范围内的排过序的值。

    第三步:调用rdd.mapPartition(function, xx, xx),function作用为把Iterator<Tuple2<Tuple,Object>>吐成Iterator<Tuple>。即再次取回Key值,此时已有序。


    Split

    POSplit的处理是直接返回第一个祖先RDD。


    LocalRearrange

    LocalRearrange -> Global Rearrange -> Package是一同出现的。



    Local rearrange直接依赖

      physicalOperator.setInputs(null);
      physicalOperator.attachInput(t);
      result = physicalOperator.getNextTuple();
    

    三步得到result。返回的Tuple格式为(index, key, value)。

    依赖POLocalRearrange本身内部对input tuple的处理。


    GlobalRearrange

    待处理的Tuple格式是(index, key, value)。最后结果为(key, { values })

    假设父RDD仅仅有一个:

    先进行按key进行一次groupBy。得到结果是Tuple2<Object, Seq<Tuple>>

    然后做一次map操作,得到(key, { values })形态的RDD,即Tuple<Object, Iterator>

    假设父RDD有多个:

    让通过rdd的map操作先将Tuple从(index, key, value)转成(key, value)形态,然后把这个rdd集合new成CoGroupRDD,包括一次(Seq) JavaConversions.asScalaBuffer(rddPairs)转化。最后调用CoGroupRDD的map方法,把Tuple2<Object,Seq<Seq<Tuple>>>转化成Tuple<Object, Iterator>,即(key, { values })形态。实际上。CoGroupRDD的map方法内部做的事情。是针对每一个Key里的Iterator集合,进行了Iterator之间的合并操作。

    Package

    Package须要把global rearrange处理后的key, Seq<Tuple>进行group。

    详细的待处理Tuple结构是这种:(key, Seq<Tuple>:{(index,key, value without key)})

    tuple.get(0)是keyTuple,tuple.get(1)是Iterator<Tuple>。最后返回(key, {values})。即Tuple<Object, Iterator>


    全文完 :)
  • 相关阅读:
    hivesql 迁移spark3.0 sparksql报错如Cannot safely cast '字段':StringType to IntegerType的问题
    sparksql 报错Container killed by YARN for exceeding memory limits. xGB of x GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead or disabling
    hiveSql 迁移spark2.4时报错Error in query: Window function row_number() requires window to be ordered, please add ORDER BY clause
    spark 执行报错 java.io.EOFException: Premature EOF from inputStream
    记录一次线上yarn RM频繁切换的故障
    Linux关闭防火墙、设置端口
    crontab 定时任务
    Yum下载rpm包、不分析依赖关系强制安装
    scp 服务器之间远程复制
    Linux 永久改变系统时间
  • 原文地址:https://www.cnblogs.com/llguanli/p/6999339.html
Copyright © 2020-2023  润新知