在Spark开发中,有时为了更好的效率,特别是涉及到关联操作的时候,对数据进行重新分区操作可以提高程序运行效率(很多时候效率的提升远远高于重新分区的消耗,所以进行重新分区还是很有价值的)。
在SparkSQL中,对数据重新分区主要有两个方法 repartition 和 coalesce ,下面将对两个方法比较

repartition

repartition 有三个重载的函数:

  • def repartition(numPartitions: Int): DataFrame 
 /**
* Returns a new [[DataFrame]] that has exactly `numPartitions` partitions.
* @group dfops
* @since 1.3.0
*/
def repartition(numPartitions: Int): DataFrame = withPlan {
Repartition(numPartitions, shuffle = true, logicalPlan)
}

此方法返回一个新的[[DataFrame]],该[[DataFrame]]具有确切的 'numpartition' 分区。

  • def repartition(partitionExprs: Column*): DataFrame 
 /**
* Returns a new [[DataFrame]] partitioned by the given partitioning expressions preserving
* the existing number of partitions. The resulting DataFrame is hash partitioned.
*
* This is the same operation as "DISTRIBUTE BY" in SQL (Hive QL).
*
* @group dfops
* @since 1.6.0
*/
@scala.annotation.varargs
def repartition(partitionExprs: Column*): DataFrame = withPlan {
RepartitionByExpression(partitionExprs.map(_.expr), logicalPlan, numPartitions = None)
}

此方法返回一个新的[[DataFrame]]分区,它由保留现有分区数量的给定分区表达式划分。得到的DataFrame是哈希分区的。

这与SQL (Hive QL)中的“distribution BY”操作相同。

  • def repartition(numPartitions: Int, partitionExprs: Column*): DataFrame
   /**
* Returns a new [[DataFrame]] partitioned by the given partitioning expressions into
* `numPartitions`. The resulting DataFrame is hash partitioned.
*
* This is the same operation as "DISTRIBUTE BY" in SQL (Hive QL).
*
* @group dfops
* @since 1.6.0
*/
@scala.annotation.varargs
def repartition(numPartitions: Int, partitionExprs: Column*): DataFrame = withPlan {
RepartitionByExpression(partitionExprs.map(_.expr), logicalPlan, Some(numPartitions))
}

此方法返回一个新的[[DataFrame]],由给定的分区表达式划分为 'numpartition' 。得到的DataFrame是哈希分区的。

这与SQL (Hive QL)中的“distribution BY”操作相同。

coalesce

  • coalesce(numPartitions: Int): DataFrame
   /**
* Returns a new [[DataFrame]] that has exactly `numPartitions` partitions.
* Similar to coalesce defined on an [[RDD]], this operation results in a narrow dependency, e.g.
* if you go from 1000 partitions to 100 partitions, there will not be a shuffle, instead each of
* the 100 new partitions will claim 10 of the current partitions.
* @group rdd
* @since 1.4.0
*/
def coalesce(numPartitions: Int): DataFrame = withPlan {
Repartition(numPartitions, shuffle = false, logicalPlan)
}

返回一个新的[[DataFrame]],该[[DataFrame]]具有确切的 'numpartition' 分区。类似于在[[RDD]]上定义的coalesce,这种操作会导致一个狭窄的依赖关系,例如:

如果从1000个分区到100个分区,就不会出现shuffle,而是100个新分区中的每一个都会声明10个当前分区。

反过来从100个分区到1000个分区,将会出现shuffle。

注:coalesce(numPartitions: Int): DataFrame    和 repartition(numPartitions: Int): DataFrame 底层调用的都是 class Repartition(numPartitions: Int, shuffle: Boolean, child: LogicalPlan)

 /**
* Returns a new RDD that has exactly `numPartitions` partitions. Differs from
* [[RepartitionByExpression]] as this method is called directly by DataFrame's, because the user
* asked for `coalesce` or `repartition`. [[RepartitionByExpression]] is used when the consumer
* of the output requires some specific ordering or distribution of the data.
*/
case class Repartition(numPartitions: Int, shuffle: Boolean, child: LogicalPlan)
extends UnaryNode {
override def output: Seq[Attribute] = child.output
}

返回一个新的RDD,该RDD恰好具有“numpartition”分区。与[[RepartitionByExpression]]不同的是,这个方法直接由DataFrame调用,因为用户需要' coalesce '或' repartition '。

当输出的使用者需要特定的数据排序或分布时使用[[RepartitionByExpression]]。(源码里面说的是RDD,但是返回类型写的是DataFrame,感觉没差)。

repartition(partitionExprs: Column*): DataFramerepartition(numPartitions: Int, partitionExprs: Column*): DataFrame 底层调用是

class RepartitionByExpression(partitionExpressions:Seq[Expression],child:LogicalPlan,numPartitions:Option[Int]=None) extends RedistributeData

 /**
* This method repartitions data using [[Expression]]s into `numPartitions`, and receives
* information about the number of partitions during execution. Used when a specific ordering or
* distribution is expected by the consumer of the query result. Use [[Repartition]] for RDD-like
* `coalesce` and `repartition`.
* If `numPartitions` is not specified, the number of partitions will be the number set by
* `spark.sql.shuffle.partitions`.
*/
case class RepartitionByExpression(
partitionExpressions: Seq[Expression],
child: LogicalPlan,
numPartitions: Option[Int] = None) extends RedistributeData {
numPartitions match {
case Some(n) => require(n > 0, "numPartitions must be greater than 0.")
case None => // Ok
}

该方法使用[[Expression]]将数据重新划分为 'numpartition',并在执行期间接收关于分区数量的信息。当用户期望某个特定的排序或分布时使用。使用[[Repartition]]用于类rdd的 'coalesce' 和 'Repartition'。

如果没有指定 'numpartition',那么分区的数量将由 "spark.sql.shuffle.partition" 设置。

使用示例

  • def repartition(numPartitions: Int): DataFrame 
 //    获取一个测试的DataFrame 里面包含一个user字段
val testDataFrame: DataFrame = readMysqlTable(sqlContext, "MYSQLTABLE", proPath)
// 获得10个分区的DataFrame
testDataFrame.repartition(10) 
  • def repartition(partitionExprs: Column*): DataFrame 
 //    获取一个测试的DataFrame 里面包含一个user字段
val testDataFrame: DataFrame = readMysqlTable(sqlContext, "MYSQLTABLE", proPath)
// 根据 user 字段进行分区,分区数量由 spark.sql.shuffle.partition 决定
testDataFrame.repartition($"user")
  • def repartition(numPartitions: Int, partitionExprs: Column*): DataFrame
 //    获取一个测试的DataFrame 里面包含一个user字段
val testDataFrame: DataFrame = readMysqlTable(sqlContext, "MYSQLTABLE", proPath)
// 根据 user 字段进行分区,将获得10个分区的DataFrame,此方法有时候在join的时候可以极大的提高效率,但是得注意出现数据倾斜的问题
testDataFrame.repartition(10,$"user") 
  • coalesce(numPartitions: Int): DataFrame
 val testDataFrame1: DataFrame = readMysqlTable(sqlContext, "MYSQLTABLE", proPath)
val testDataFrame2=testDataFrame1.repartition(10)
// 不会触发shuffle
testDataFrame2.coalesce(5)
// 触发shuffle 返回一个100分区的DataFrame
testDataFrame2.coalesce(100)  

至于分区的数据设定,得根据自己的实际情况来,多了浪费少了负优化。

现在的只是初步探讨,具体的底层代码实现,后续去研究一下。

此文为本人工作学习整理笔记,转载请注明出处!!!!!!

Spark源码系列:DataFrame repartition、coalesce 对比的更多相关文章

  1. Spark源码系列:RDD repartition、coalesce 对比

    在上一篇文章中 Spark源码系列:DataFrame repartition.coalesce 对比 对DataFrame的repartition.coalesce进行了对比,在这篇文章中,将会对R ...

  2. Spark源码系列(五)分布式缓存

    这一章想讲一下Spark的缓存是如何实现的.这个persist方法是在RDD里面的,所以我们直接打开RDD这个类. def persist(newLevel: StorageLevel): this. ...

  3. Spark源码系列(一)spark-submit提交作业过程

    前言 折腾了很久,终于开始学习Spark的源码了,第一篇我打算讲一下Spark作业的提交过程. 这个是Spark的App运行图,它通过一个Driver来和集群通信,集群负责作业的分配.今天我要讲的是如 ...

  4. Spark源码系列(九)Spark SQL初体验之解析过程详解

    好久没更新博客了,之前学了一些R语言和机器学习的内容,做了一些笔记,之后也会放到博客上面来给大家共享.一个月前就打算更新Spark Sql的内容了,因为一些别的事情耽误了,今天就简单写点,Spark1 ...

  5. Spark源码系列(八)Spark Streaming实例分析

    这一章要讲Spark Streaming,讲之前首先回顾下它的用法,具体用法请参照<Spark Streaming编程指南>. Example代码分析 val ssc = )); // 获 ...

  6. Spark源码系列(七)Spark on yarn具体实现

    本来不打算写的了,但是真的是闲来无事,整天看美剧也没啥意思.这一章打算讲一下Spark on yarn的实现,1.0.0里面已经是一个stable的版本了,可是1.0.1也出来了,离1.0.0发布才一 ...

  7. Spark源码系列(六)Shuffle的过程解析

    Spark大会上,所有的演讲嘉宾都认为shuffle是最影响性能的地方,但是又无可奈何.之前去百度面试hadoop的时候,也被问到了这个问题,直接回答了不知道. 这篇文章主要是沿着下面几个问题来开展: ...

  8. Spark源码系列(四)图解作业生命周期

    这一章我们探索了Spark作业的运行过程,但是没把整个过程描绘出来,好,跟着我走吧,let you know! 我们先回顾一下这个图,Driver Program是我们写的那个程序,它的核心是Spar ...

  9. Spark源码系列(三)作业运行过程

    作业执行 上一章讲了RDD的转换,但是没讲作业的运行,它和Driver Program的关系是啥,和RDD的关系是啥? 官方给的例子里面,一执行collect方法就能出结果,那我们就从collect开 ...

随机推荐

  1. 白鹭wing的自动编译

    1.lunch.json文件中的"preLaunchTask":"build",注释掉. 2.安装node-v6.11.2-x64.msi 3.安装tsc 命令 ...

  2. erlang证书加密

    -module(...). -include("ewp.hrl").-include("backend.hrl").-include_lib("pub ...

  3. 第六次作业———numpy数据集练习

    1. 安装scipy,numpy,sklearn包 2. 从sklearn包自带的数据集中读出鸢尾花数据集data 3.查看data类型,包含哪些数据 4.取出鸢尾花特征和鸢尾花类别数据,查看其形状及 ...

  4. Python收发邮件

    发送邮件使用SMTP协议,接受POP3或IMAP: 创建邮件内容email模块,发送邮件smtplib模块.发送邮件比较简单,只需先创建SMTP对象,登录服务器后根据发收邮箱地址发送即可: POP3接 ...

  5. JDK 1.8 -> 1.7

    电脑刚开始装的是1.8 version, 然后又需要用到1.7. 所以就要把1.8 降为1.7, 网上有很多说把1.8删掉,这种做法我是不建议的,那么要用的时候呢?又得装回来多蛋疼.. JDK 1.8 ...

  6. 小程序 map组件问题 cover-view问题

    使用小程序的组件map时 在开发者工具上一切顺利 但是在真机预览时 发现地图的层级是最高的 任何标签都覆盖不了它 调整z-index值并没有什么效果 原因是 微信小程序的map.video.canva ...

  7. vue跳坑笔记

    序号 报错截图 关键词 解决办法 1 - code EPERM errno 4048 syscall scandir operation not permitted 清除npm缓存,命令: npm c ...

  8. 前端特效demo | 一起围观 10 种创意时钟

    时钟动画应用非常广泛,我们经常会看到一些相当个性化的HTML5时钟动画.今天我们向大家分享 10 款形态各异的超时尚时钟动画,其中有圆盘时钟.创意时钟.电子时钟等,希望大家会喜欢. 文内附有时钟效果代 ...

  9. 最近学习的 Node.js 数组_函数

    数组的排序,用到了箭头函数 let arr=[, , , , , , ]; /* arr.sort(function (n1, n2){ return n1-n2; }); */ // 等价于上面的写 ...

  10. Shell 函数相关

    一.函数的两种定义方式 第一种:函数名(){ ...... } 第二种:function 函数名{ ...... } 调用时直接使用 函数名 调用,将函数当作一个“命令”即可.函数内部直接使用 $1. ...