/** Spark SQL源码分析系列文章*/

前面几篇文章主要介绍的是spark sql包里的的spark sql执行流程,以及Catalyst包内的SqlParserAnalyzerOptimizer,最后要介绍一下Catalyst里最后的一个Plan了,即Physical Plan。物理计划是Spark SQL执行Spark job的前置,也是最后一道计划。

如图:

一、SparkPlanner

话接上回,Optimizer接受输入的Analyzed Logical Plan后,会有SparkPlanner来对Optimized Logical Plan进行转换,生成Physical plans。

  1. lazy val optimizedPlan = optimizer(analyzed)
  2. // TODO: Don't just pick the first one...
  3. lazy val sparkPlan = planner(optimizedPlan).next()

SparkPlanner的apply方法,会返回一个Iterator[PhysicalPlan]。
  SparkPlanner继承了SparkStrategies,SparkStrategies继承了QueryPlanner。
  SparkStrategies包含了一系列特定的Strategies,这些Strategies是继承自QueryPlanner中定义的Strategy,它定义接受一个Logical Plan,生成一系列的Physical Plan

  1. @transient
  2. protected[sql] val planner = new SparkPlanner
  3. protected[sql] class SparkPlanner extends SparkStrategies {
  4. val sparkContext: SparkContext = self.sparkContext
  5. val sqlContext: SQLContext = self
  6. def numPartitions = self.numShufflePartitions //partitions的个数
  7. val strategies: Seq[Strategy] =  //策略的集合
  8. CommandStrategy(self) ::
  9. TakeOrdered ::
  10. PartialAggregation ::
  11. LeftSemiJoin ::
  12. HashJoin ::
  13. InMemoryScans ::
  14. ParquetOperations ::
  15. BasicOperators ::
  16. CartesianProduct ::
  17. BroadcastNestedLoopJoin :: Nil
  18. etc......
  19. }

QueryPlanner 是SparkPlanner的基类,定义了一系列的关键点,如Strategy,planLater和apply。

  1. abstract class QueryPlanner[PhysicalPlan <: TreeNode[PhysicalPlan]] {
  2. /** A list of execution strategies that can be used by the planner */
  3. def strategies: Seq[Strategy]
  4. /**
  5. * Given a [[plans.logical.LogicalPlan LogicalPlan]], returns a list of `PhysicalPlan`s that can
  6. * be used for execution. If this strategy does not apply to the give logical operation then an
  7. * empty list should be returned.
  8. */
  9. abstract protected class Strategy extends Logging {
  10. def apply(plan: LogicalPlan): Seq[PhysicalPlan]  //接受一个logical plan,返回Seq[PhysicalPlan]
  11. }
  12. /**
  13. * Returns a placeholder for a physical plan that executes `plan`. This placeholder will be
  14. * filled in automatically by the QueryPlanner using the other execution strategies that are
  15. * available.
  16. */
  17. protected def planLater(plan: LogicalPlan) = apply(plan).next() //返回一个占位符,占位符会自动被QueryPlanner用其它的strategies apply
  18. def apply(plan: LogicalPlan): Iterator[PhysicalPlan] = {
  19. // Obviously a lot to do here still...
  20. val iter = strategies.view.flatMap(_(plan)).toIterator //整合所有的Strategy,_(plan)每个Strategy应用plan上,得到所有Strategies执行完后生成的所有Physical Plan的集合,一个iter
  21. assert(iter.hasNext, s"No plan for $plan")
  22. iter //返回所有物理计划
  23. }
  24. }

继承关系:

二、Spark Plan

 Spark Plan是Catalyst里经过所有Strategies apply 的最终的物理执行计划的抽象类,它只是用来执行spark job的。
  1. lazy val executedPlan: SparkPlan = prepareForExecution(sparkPlan)

prepareForExecution其实是一个RuleExecutor[SparkPlan],当然这里的Rule就是SparkPlan了。

  1. @transient
  2. protected[sql] val prepareForExecution = new RuleExecutor[SparkPlan] {
  3. val batches =
  4. Batch("Add exchange", Once, AddExchange(self)) :: //添加shuffler操作如果必要的话
  5. Batch("Prepare Expressions", Once, new BindReferences[SparkPlan]) :: Nil //Bind references
  6. }

Spark Plan继承Query Plan[Spark Plan],里面定义的partition,requiredChildDistribution以及spark sql启动执行的execute方法。

  1. abstract class SparkPlan extends QueryPlan[SparkPlan] with Logging {
  2. self: Product =>
  3. // TODO: Move to `DistributedPlan`
  4. /** Specifies how data is partitioned across different nodes in the cluster. */
  5. def outputPartitioning: Partitioning = UnknownPartitioning(0) // TODO: WRONG WIDTH!
  6. /** Specifies any partition requirements on the input data for this operator. */
  7. def requiredChildDistribution: Seq[Distribution] =
  8. Seq.fill(children.size)(UnspecifiedDistribution)
  9. /**
  10. * Runs this query returning the result as an RDD.
  11. */
  12. def execute(): RDD[Row]  //真正执行查询的方法execute,返回的是一个RDD
  13. /**
  14. * Runs this query returning the result as an array.
  15. */
  16. def executeCollect(): Array[Row] = execute().map(_.copy()).collect() //exe & collect
  17. protected def buildRow(values: Seq[Any]): Row =  //根据当前的值,生成Row对象,其实是一个封装了Array的对象。
  18. new GenericRow(values.toArray)
  19. }

关于Spark Plan的继承关系,如图:

三、Strategies

  Strategy,注意这里Strategy是在execution包下的,在SparkPlanner里定义了目前的几种策略:
  LeftSemiJoin、HashJoin、PartialAggregation、BroadcastNestedLoopJoin、CartesianProduct、TakeOrdered、ParquetOperations、InMemoryScans、BasicOperators、CommandStrategy

3.1、LeftSemiJoin

Join分为好几种类型:
  1. case object Inner extends JoinType
  2. case object LeftOuter extends JoinType
  3. case object RightOuter extends JoinType
  4. case object FullOuter extends JoinType
  5. case object LeftSemi extends JoinType

如果Logical Plan里的Join是joinType为LeftSemi的话,就会执行这种策略,
  这里ExtractEquiJoinKeys是一个pattern定义在patterns.scala里,主要是做模式匹配用的。
  这里匹配只要是等值的join操作,都会封装为ExtractEquiJoinKeys对象,它会解析当前join,最后返回(joinType, rightKeys, leftKeys, condition, leftChild, rightChild)的格式。
  最后返回一个execution.LeftSemiJoinHash这个Spark Plan,可见Spark Plan的类图继承关系图。

  1. object LeftSemiJoin extends Strategy with PredicateHelper {
  2. def apply(plan: LogicalPlan): Seq[SparkPlan] = plan match {
  3. // Find left semi joins where at least some predicates can be evaluated by matching join keys
  4. case ExtractEquiJoinKeys(LeftSemi, leftKeys, rightKeys, condition, left, right) =>
  5. val semiJoin = execution.LeftSemiJoinHash(  //根据解析后的Join,实例化execution.LeftSemiJoinHash这个Spark Plan 返回
  6. leftKeys, rightKeys, planLater(left), planLater(right))
  7. condition.map(Filter(_, semiJoin)).getOrElse(semiJoin) :: Nil
  8. // no predicate can be evaluated by matching hash keys
  9. case logical.Join(left, right, LeftSemi, condition) =>  //没有Join key的,即非等值join连接的,返回LeftSemiJoinBNL这个Spark Plan
  10. execution.LeftSemiJoinBNL(
  11. planLater(left), planLater(right), condition)(sqlContext) :: Nil
  12. case _ => Nil
  13. }
  14. }

3.2、HashJoin

HashJoin是我们最见的操作,innerJoin类型,里面提供了2种Spark Plan,BroadcastHashJoin 和 ShuffledHashJoin
  BroadcastHashJoin的实现是一种广播变量的实现方法,如果设置了spark.sql.join.broadcastTables这个参数的表(表面逗号隔开)
  就会用spark的Broadcast Variables方式先将一张表给查询出来,然后广播到各个机器中,相当于Hive中的map join。
  ShuffledHashJoin是一种最传统的默认的join方式,会根据shuffle key进行shuffle的hash join。

  1. object HashJoin extends Strategy with PredicateHelper {
  2. private[this] def broadcastHashJoin(
  3. leftKeys: Seq[Expression],
  4. rightKeys: Seq[Expression],
  5. left: LogicalPlan,
  6. right: LogicalPlan,
  7. condition: Option[Expression],
  8. side: BuildSide) = {
  9. val broadcastHashJoin = execution.BroadcastHashJoin(
  10. leftKeys, rightKeys, side, planLater(left), planLater(right))(sqlContext)
  11. condition.map(Filter(_, broadcastHashJoin)).getOrElse(broadcastHashJoin) :: Nil
  12. }
  13. def broadcastTables: Seq[String] = sqlContext.joinBroadcastTables.split(",").toBuffer //获取需要广播的表
  14. def apply(plan: LogicalPlan): Seq[SparkPlan] = plan match {
  15. case ExtractEquiJoinKeys(
  16. Inner,
  17. leftKeys,
  18. rightKeys,
  19. condition,
  20. left,
  21. right @ PhysicalOperation(_, _, b: BaseRelation))
  22. if broadcastTables.contains(b.tableName) => //如果右孩子是广播的表,则buildSide取BuildRight
  23. broadcastHashJoin(leftKeys, rightKeys, left, right, condition, BuildRight)
  24. case ExtractEquiJoinKeys(
  25. Inner,
  26. leftKeys,
  27. rightKeys,
  28. condition,
  29. left @ PhysicalOperation(_, _, b: BaseRelation),
  30. right)
  31. if broadcastTables.contains(b.tableName) =>//如果左孩子是广播的表,则buildSide取BuildLeft
  32. broadcastHashJoin(leftKeys, rightKeys, left, right, condition, BuildLeft)
  33. case ExtractEquiJoinKeys(Inner, leftKeys, rightKeys, condition, left, right) =>
  34. val hashJoin =
  35. execution.ShuffledHashJoin( //根据hash key shuffle的 Hash Join
  36. leftKeys, rightKeys, BuildRight, planLater(left), planLater(right))
  37. condition.map(Filter(_, hashJoin)).getOrElse(hashJoin) :: Nil
  38. case _ => Nil
  39. }
  40. }

3.3、PartialAggregation

  PartialAggregation是一个部分聚合的策略,即有些聚合操作可以在local里面完成的,就在local data里完成,而不必要的去shuffle所有的字段。
  1. object PartialAggregation extends Strategy {
  2. def apply(plan: LogicalPlan): Seq[SparkPlan] = plan match {
  3. case logical.Aggregate(groupingExpressions, aggregateExpressions, child) =>
  4. // Collect all aggregate expressions.
  5. val allAggregates =
  6. aggregateExpressions.flatMap(_ collect { case a: AggregateExpression => a })
  7. // Collect all aggregate expressions that can be computed partially.
  8. val partialAggregates =
  9. aggregateExpressions.flatMap(_ collect { case p: PartialAggregate => p })
  10. // Only do partial aggregation if supported by all aggregate expressions.
  11. if (allAggregates.size == partialAggregates.size) {
  12. // Create a map of expressions to their partial evaluations for all aggregate expressions.
  13. val partialEvaluations: Map[Long, SplitEvaluation] =
  14. partialAggregates.map(a => (a.id, a.asPartial)).toMap
  15. // We need to pass all grouping expressions though so the grouping can happen a second
  16. // time. However some of them might be unnamed so we alias them allowing them to be
  17. // referenced in the second aggregation.
  18. val namedGroupingExpressions: Map[Expression, NamedExpression] = groupingExpressions.map {
  19. case n: NamedExpression => (n, n)
  20. case other => (other, Alias(other, "PartialGroup")())
  21. }.toMap
  22. // Replace aggregations with a new expression that computes the result from the already
  23. // computed partial evaluations and grouping values.
  24. val rewrittenAggregateExpressions = aggregateExpressions.map(_.transformUp {
  25. case e: Expression if partialEvaluations.contains(e.id) =>
  26. partialEvaluations(e.id).finalEvaluation
  27. case e: Expression if namedGroupingExpressions.contains(e) =>
  28. namedGroupingExpressions(e).toAttribute
  29. }).asInstanceOf[Seq[NamedExpression]]
  30. val partialComputation =
  31. (namedGroupingExpressions.values ++
  32. partialEvaluations.values.flatMap(_.partialEvaluations)).toSeq
  33. // Construct two phased aggregation.
  34. execution.Aggregate( //返回execution.Aggregate这个Spark Plan
  35. partial = false,
  36. namedGroupingExpressions.values.map(_.toAttribute).toSeq,
  37. rewrittenAggregateExpressions,
  38. execution.Aggregate(
  39. partial = true,
  40. groupingExpressions,
  41. partialComputation,
  42. planLater(child))(sqlContext))(sqlContext) :: Nil
  43. } else {
  44. Nil
  45. }
  46. case _ => Nil
  47. }
  48. }

3.4、BroadcastNestedLoopJoin

  BroadcastNestedLoopJoin是用于Left Outer Join, RightOuter, FullOuter这三种类型的join
 而上述的Hash Join仅仅用于InnerJoin,这点要区分开来。
  1. object BroadcastNestedLoopJoin extends Strategy {
  2. def apply(plan: LogicalPlan): Seq[SparkPlan] = plan match {
  3. case logical.Join(left, right, joinType, condition) =>
  4. execution.BroadcastNestedLoopJoin(
  5. planLater(left), planLater(right), joinType, condition)(sqlContext) :: Nil
  6. case _ => Nil
  7. }
  8. }

部分代码;

  1. if (!matched && (joinType == LeftOuter || joinType == FullOuter)) {  //LeftOuter or FullOuter
  2. matchedRows += buildRow(streamedRow ++ Array.fill(right.output.size)(null))
  3. }
  4. }
  5. Iterator((matchedRows, includedBroadcastTuples))
  6. }
  7. val includedBroadcastTuples = streamedPlusMatches.map(_._2)
  8. val allIncludedBroadcastTuples =
  9. if (includedBroadcastTuples.count == 0) {
  10. new scala.collection.mutable.BitSet(broadcastedRelation.value.size)
  11. } else {
  12. streamedPlusMatches.map(_._2).reduce(_ ++ _)
  13. }
  14. val rightOuterMatches: Seq[Row] =
  15. if (joinType == RightOuter || joinType == FullOuter) { //RightOuter or FullOuter
  16. broadcastedRelation.value.zipWithIndex.filter {
  17. case (row, i) => !allIncludedBroadcastTuples.contains(i)
  18. }.map {
  19. // TODO: Use projection.
  20. case (row, _) => buildRow(Vector.fill(left.output.size)(null) ++ row)
  21. }
  22. } else {
  23. Vector()
  24. }

3.5、CartesianProduct

  1. 笛卡尔积的Join,有待过滤条件的Join。
  2. 主要是利用RDD的cartesian实现的。
  3. object CartesianProduct extends Strategy {
  4. def apply(plan: LogicalPlan): Seq[SparkPlan] = plan match {
  5. case logical.Join(left, right, _, None) =>
  6. execution.CartesianProduct(planLater(left), planLater(right)) :: Nil
  7. case logical.Join(left, right, Inner, Some(condition)) =>
  8. execution.Filter(condition,
  9. execution.CartesianProduct(planLater(left), planLater(right))) :: Nil
  10. case _ => Nil
  11. }
  12. }

3.6、TakeOrdered

TakeOrdered是用于Limit操作的,如果有Limit和Sort操作。
  则返回一个TakeOrdered的Spark Plan。
  主要也是利用RDD的takeOrdered方法来实现的排序后取TopN。

  1. object TakeOrdered extends Strategy {
  2. def apply(plan: LogicalPlan): Seq[SparkPlan] = plan match {
  3. case logical.Limit(IntegerLiteral(limit), logical.Sort(order, child)) =>
  4. execution.TakeOrdered(limit, order, planLater(child))(sqlContext) :: Nil
  5. case _ => Nil
  6. }
  7. }

3.7、ParquetOperations

支持ParquetOperations的读写,插入Table等。
  1. object ParquetOperations extends Strategy {
  2. def apply(plan: LogicalPlan): Seq[SparkPlan] = plan match {
  3. // TODO: need to support writing to other types of files.  Unify the below code paths.
  4. case logical.WriteToFile(path, child) =>
  5. val relation =
  6. ParquetRelation.create(path, child, sparkContext.hadoopConfiguration)
  7. // Note: overwrite=false because otherwise the metadata we just created will be deleted
  8. InsertIntoParquetTable(relation, planLater(child), overwrite=false)(sqlContext) :: Nil
  9. case logical.InsertIntoTable(table: ParquetRelation, partition, child, overwrite) =>
  10. InsertIntoParquetTable(table, planLater(child), overwrite)(sqlContext) :: Nil
  11. case PhysicalOperation(projectList, filters: Seq[Expression], relation: ParquetRelation) =>
  12. val prunePushedDownFilters =
  13. if (sparkContext.conf.getBoolean(ParquetFilters.PARQUET_FILTER_PUSHDOWN_ENABLED, true)) {
  14. (filters: Seq[Expression]) => {
  15. filters.filter { filter =>
  16. // Note: filters cannot be pushed down to Parquet if they contain more complex
  17. // expressions than simple "Attribute cmp Literal" comparisons. Here we remove
  18. // all filters that have been pushed down. Note that a predicate such as
  19. // "(A AND B) OR C" can result in "A OR C" being pushed down.
  20. val recordFilter = ParquetFilters.createFilter(filter)
  21. if (!recordFilter.isDefined) {
  22. // First case: the pushdown did not result in any record filter.
  23. true
  24. } else {
  25. // Second case: a record filter was created; here we are conservative in
  26. // the sense that even if "A" was pushed and we check for "A AND B" we
  27. // still want to keep "A AND B" in the higher-level filter, not just "B".
  28. !ParquetFilters.findExpression(recordFilter.get, filter).isDefined
  29. }
  30. }
  31. }
  32. } else {
  33. identity[Seq[Expression]] _
  34. }
  35. pruneFilterProject(
  36. projectList,
  37. filters,
  38. prunePushedDownFilters,
  39. ParquetTableScan(_, relation, filters)(sqlContext)) :: Nil
  40. case _ => Nil
  41. }
  42. }

3.8、InMemoryScans

  InMemoryScans主要是对InMemoryRelation这个Logical Plan操作。
  调用的其实是Spark Planner里的pruneFilterProject这个方法。
  1. object InMemoryScans extends Strategy {
  2. def apply(plan: LogicalPlan): Seq[SparkPlan] = plan match {
  3. case PhysicalOperation(projectList, filters, mem: InMemoryRelation) =>
  4. pruneFilterProject(
  5. projectList,
  6. filters,
  7. identity[Seq[Expression]], // No filters are pushed down.
  8. InMemoryColumnarTableScan(_, mem)) :: Nil
  9. case _ => Nil
  10. }
  11. }

3.9、BasicOperators

所有定义在org.apache.spark.sql.execution里的基本的Spark Plan,它们都在org.apache.spark.sql.execution包下basicOperators.scala内的
  有Project、Filter、Sample、Union、Limit、TakeOrdered、Sort、ExistingRdd。
  这些是基本元素,实现都相对简单,基本上都是RDD里的方法来实现的。

  1. object BasicOperators extends Strategy {
  2. def numPartitions = self.numPartitions
  3. def apply(plan: LogicalPlan): Seq[SparkPlan] = plan match {
  4. case logical.Distinct(child) =>
  5. execution.Aggregate(
  6. partial = false, child.output, child.output, planLater(child))(sqlContext) :: Nil
  7. case logical.Sort(sortExprs, child) =>
  8. // This sort is a global sort. Its requiredDistribution will be an OrderedDistribution.
  9. execution.Sort(sortExprs, global = true, planLater(child)):: Nil
  10. case logical.SortPartitions(sortExprs, child) =>
  11. // This sort only sorts tuples within a partition. Its requiredDistribution will be
  12. // an UnspecifiedDistribution.
  13. execution.Sort(sortExprs, global = false, planLater(child)) :: Nil
  14. case logical.Project(projectList, child) =>
  15. execution.Project(projectList, planLater(child)) :: Nil
  16. case logical.Filter(condition, child) =>
  17. execution.Filter(condition, planLater(child)) :: Nil
  18. case logical.Aggregate(group, agg, child) =>
  19. execution.Aggregate(partial = false, group, agg, planLater(child))(sqlContext) :: Nil
  20. case logical.Sample(fraction, withReplacement, seed, child) =>
  21. execution.Sample(fraction, withReplacement, seed, planLater(child)) :: Nil
  22. case logical.LocalRelation(output, data) =>
  23. val dataAsRdd =
  24. sparkContext.parallelize(data.map(r =>
  25. new GenericRow(r.productIterator.map(convertToCatalyst).toArray): Row))
  26. execution.ExistingRdd(output, dataAsRdd) :: Nil
  27. case logical.Limit(IntegerLiteral(limit), child) =>
  28. execution.Limit(limit, planLater(child))(sqlContext) :: Nil
  29. case Unions(unionChildren) =>
  30. execution.Union(unionChildren.map(planLater))(sqlContext) :: Nil
  31. case logical.Generate(generator, join, outer, _, child) =>
  32. execution.Generate(generator, join = join, outer = outer, planLater(child)) :: Nil
  33. case logical.NoRelation =>
  34. execution.ExistingRdd(Nil, singleRowRdd) :: Nil
  35. case logical.Repartition(expressions, child) =>
  36. execution.Exchange(HashPartitioning(expressions, numPartitions), planLater(child)) :: Nil
  37. case SparkLogicalPlan(existingPlan, _) => existingPlan :: Nil
  38. case _ => Nil
  39. }
  40. }

3.10 CommandStrategy

  CommandStrategy是专门针对Command类型的Logical Plan
  即set key = value 、 explain sql、 cache table xxx 这类操作
  SetCommand主要实现方式是SparkContext的参数
  ExplainCommand主要实现方式是利用executed Plan打印出tree string
  CacheCommand主要实现方式SparkContext的cache table和uncache table

  1. case class CommandStrategy(context: SQLContext) extends Strategy {
  2. def apply(plan: LogicalPlan): Seq[SparkPlan] = plan match {
  3. case logical.SetCommand(key, value) =>
  4. Seq(execution.SetCommand(key, value, plan.output)(context))
  5. case logical.ExplainCommand(logicalPlan) =>
  6. Seq(execution.ExplainCommand(logicalPlan, plan.output)(context))
  7. case logical.CacheCommand(tableName, cache) =>
  8. Seq(execution.CacheCommand(tableName, cache)(context))
  9. case _ => Nil
  10. }
  11. }

四、Execution

Spark Plan的Execution方式均为调用其execute()方法生成RDD,除了简单的基本操作例如上面的basic operator实现比较简单,其它的实现都比较复杂,大致的实现我都在上面介绍了,本文就不详细讨论了。

五、总结

本文从介绍了Spark SQL的Catalyst框架的Physical plan以及其如何从Optimized Logical Plan转化为Spark Plan的过程,这个过程用到了很多的物理计划策略Strategies,每个Strategies最后还是在RuleExecutor里面被执行,最后生成一系列物理计划Executed Spark Plans。
  Spark Plan是执行前最后一种计划,当生成executed spark plan后,就可以调用collect()方法来启动Spark Job来进行Spark SQL的真正执行了。
——EOF——

 

原创文章,转载请注明:

转载自:OopsOutOfMemory盛利的Blog,作者: OopsOutOfMemory

本文链接地址:http://blog.csdn.net/oopsoom/article/details/38235247

注:本文基于署名-非商业性使用-禁止演绎 2.5 中国大陆(CC BY-NC-ND 2.5 CN)协议,欢迎转载、转发和评论,但是请保留本文作者署名和文章链接。如若需要用于商业目的或者与授权方面的协商,请联系我。

转自:http://blog.csdn.net/oopsoom/article/details/38235247

第六篇:Spark SQL Catalyst源码分析之Physical Plan的更多相关文章

  1. 第五篇:Spark SQL Catalyst源码分析之Optimizer

    /** Spark SQL源码分析系列文章*/ 前几篇文章介绍了Spark SQL的Catalyst的核心运行流程.SqlParser,和Analyzer 以及核心类库TreeNode,本文将详细讲解 ...

  2. 第四篇:Spark SQL Catalyst源码分析之TreeNode Library

    /** Spark SQL源码分析系列文章*/ 前几篇文章介绍了Spark SQL的Catalyst的核心运行流程.SqlParser,和Analyzer,本来打算直接写Optimizer的,但是发现 ...

  3. 第三篇:Spark SQL Catalyst源码分析之Analyzer

    /** Spark SQL源码分析系列文章*/ 前面几篇文章讲解了Spark SQL的核心执行流程和Spark SQL的Catalyst框架的Sql Parser是怎样接受用户输入sql,经过解析生成 ...

  4. 第八篇:Spark SQL Catalyst源码分析之UDF

    /** Spark SQL源码分析系列文章*/ 在SQL的世界里,除了官方提供的常用的处理函数之外,一般都会提供可扩展的对外自定义函数接口,这已经成为一种事实的标准. 在前面Spark SQL源码分析 ...

  5. 第二篇:Spark SQL Catalyst源码分析之SqlParser

    /** Spark SQL源码分析系列文章*/ Spark SQL的核心执行流程我们已经分析完毕,可以参见Spark SQL核心执行流程,下面我们来分析执行流程中各个核心组件的工作职责. 本文先从入口 ...

  6. 第七篇:Spark SQL 源码分析之Physical Plan 到 RDD的具体实现

    /** Spark SQL源码分析系列文章*/ 接上一篇文章Spark SQL Catalyst源码分析之Physical Plan,本文将介绍Physical Plan的toRDD的具体实现细节: ...

  7. Spark Scheduler模块源码分析之TaskScheduler和SchedulerBackend

    本文是Scheduler模块源码分析的第二篇,第一篇Spark Scheduler模块源码分析之DAGScheduler主要分析了DAGScheduler.本文接下来结合Spark-1.6.0的源码继 ...

  8. 【原】Spark中Client源码分析(二)

    继续前一篇的内容.前一篇内容为: Spark中Client源码分析(一)http://www.cnblogs.com/yourarebest/p/5313006.html DriverClient中的 ...

  9. 【原】Spark中Master源码分析(二)

    继续上一篇的内容.上一篇的内容为: Spark中Master源码分析(一) http://www.cnblogs.com/yourarebest/p/5312965.html 4.receive方法, ...

随机推荐

  1. combined with the Referer header, to potentially build an exhaustive data set of user profiles and browsing habits Client Identification

    w https://www.zhihu.com/question/35307626 w 0-客户端(附加用户信息)首次请求服务端--->服务端生成session(有唯一性).session_id ...

  2. SpringBoot与消息(RabbitMQ)

    1. JMS和AMQP JMS(Java Message Service): ActiveMQ是JMS实现; AMQP(Advanced Message Queuing Protocol) 兼容JMS ...

  3. react 日期

    1.首先安装moment : npm install moment --save 2.在文件中引用: import moment from 'moment' 3.使用方式: 当前时间:moment() ...

  4. JavaWeb-Servlet-通过servlet生成验证码图片

    BufferedImage类 创建一个BufferImage servlet,用来生成验证码图片: package com.fpc; import java.awt.Color; import jav ...

  5. bat批处理异备文件、压缩文件

    1.压缩本地文件,并把压缩后的文件复制到其他机器 net use Z: \\192.168.135.1\share_linux a123456! /user:chaoqun.guo set bath= ...

  6. java实现简单的数据库的增删查改,并布局交互界面

        一.系统简介 1.1.简介  本系统提供了学生信息管理中常见的基本功能,主要包括管理员.管理员的主要功能有对学生信息进行增加.删除.修改.查找等操作,对信息进行管理,对信息进行修改.查找等操作 ...

  7. 手把手教你使用 GitBook

    一.简介 GitBook 是一个支持用 MarkDown 编写文档的软件,支持输出 HTML.PDF.eBook 格式文档.作为开发者我们一般会用它来写一些技术和接口文档. GitBook 官网:ht ...

  8. 漫谈DOM 事件流的三个阶段

    一丶 流 什么是流? 比如 react 中的单项数据流,Node.js 中的流,或者本文中的 DOM 事件流,都是流的具体体现.专业地讲,流是程序输入或输出的一个连续的字节序列:通俗地讲,流是有方向的 ...

  9. Linux系统——ACL权限控制及特殊权限

    ACL权限控制 ACL(access control list),可以提供除属主.属组.其他人的rwx权限之外的细节权限设定 ACL的权限控制 (1)User 使用者 (2)Group 群组 (3)M ...

  10. 技术分享会(二):SQLSERVER索引介绍

    SQLSERVER索引介绍 一.SQLSERVER索引类型? 1.聚集索引: 2.非聚集索引: 3.包含索引: 4.列存储索引: 5.无索引(堆表): 二.如何创建索引? 索引示例: 建表 creat ...