spark shuffle 相关细节整理
1.Shuffle Write 和Shuffle Read具体发生在哪里
2.哪里用到了Partitioner
3.何为mapSideCombine
4.何时进行排序
之前已经看过spark shuffle源码了,现在总结一下一些之前没有理解的小知识点,作为一个总结。
用户自定义的Partitioner存到了哪里?
假设用户在调用reduceByKey时,传递了一个自定义的Partitioner,那么,这个Partitioner会被保存到ShuffleRDD的ShuffleDependency中。在进行Shuffle Write时,会使用这个Partitioner来对finalRDD.iterator(partition)的计算结果shuffle到不同的Bucket中。
何为mapSideCombine
reduceByKey默认是开启了mapSideCombine的,在进行shuffle write时会进行本地聚合,在shuffle read时,也会合并一下。举一个例子更好:
shuffle write阶段:
partition0:[(hello,1),(hello,1)]
partition1:[(hello,1),(word,1),(word,1)]
mapSideCombine后:
partition0:[(hello,2)]
partition1:[(hello,1),(word,2)]
hash shuffle后:
[(hello,2),(hello,1)]
[(word,2)]
hash read阶段:
[(hello,3)]
[(word,2)]
何时排序
排序操作发生在shuffle read 阶段。在shuffle read 进行完mapSideCombine之后,就开始进行排序了。
reduceByKey做了什么?
假设我们对rdd1调用了reduceByKey,那么最终的RDD依赖关系如下:rdd1->ShuffleRDD。rdd1.reduceByKey中,会做如下非常重要的事情:创建ShuffleRDD,在创建ShuffleRDD的过程中最最最重要的就是会创建ShuffleDependency,这个ShuffleDependency中有Aggregator,Partitioner,Ordering,parentRDD,mapSideCombine等重要的信息。为什么说ShuffleDependency非常重要,因为他是沟通Shuffle Writer和Shuffle Reader的一个重要桥梁。
Shuffle Write
Shuffle Write 发生在ShuffleMapTask.runTask中。首先反序列出rdd1和那个ShuffleDependency:(rdd1,dep),然后调用rdd1.iterator(partition)获取计算结果,再对计算结果进行ShuffleWriter,代码如下:
- override def runTask(context: TaskContext): MapStatus = {
- // Deserialize the RDD using the broadcast variable.
- val deserializeStartTime = System.currentTimeMillis()
- val ser = SparkEnv.get.closureSerializer.newInstance()
- //统计反序列化rdd和shuffleDependency的时间
- val (rdd, dep) = ser.deserialize[(RDD[_], ShuffleDependency[_, _, _])](
- ByteBuffer.wrap(taskBinary.value), Thread.currentThread.getContextClassLoader)
- _executorDeserializeTime = System.currentTimeMillis() - deserializeStartTime
- metrics = Some(context.taskMetrics)
- var writer: ShuffleWriter[Any, Any] = null
- try {
- val manager = SparkEnv.get.shuffleManager
- writer = manager.getWriter[Any, Any](dep.shuffleHandle, partitionId, context)
- writer.write(rdd.iterator(partition, context).asInstanceOf[Iterator[_ <: Product2[Any, Any]]])
- return writer.stop(success = true).get
- } catch {
- case e: Exception =>
- try {
- if (writer != null) {
- writer.stop(success = false)
- }
- } catch {
- case e: Exception =>
- log.debug("Could not stop writer", e)
- }
- throw e
- }
- }
我们以HashSuffleWriter为例,在其write(),他就会用到mapSideCombine和Partitioner。如下:
- /** Write a bunch of records to this task's output */
- override def write(records: Iterator[Product2[K, V]]): Unit = {
- val iter = if (dep.aggregator.isDefined) {
- if (dep.mapSideCombine) {
- dep.aggregator.get.combineValuesByKey(records, context)
- } else {
- records
- }
- } else {
- require(!dep.mapSideCombine, "Map-side combine without Aggregator specified!")
- records
- }
- for (elem <- iter) {
- val bucketId = dep.partitioner.getPartition(elem._1)
- shuffle.writers(bucketId).write(elem._1, elem._2)
- }
- }
Shuffle Read
shuffle Read发生在ShuffleRDD的compute中:
- override def compute(split: Partition, context: TaskContext): Iterator[(K, C)] = {
- val dep = dependencies.head.asInstanceOf[ShuffleDependency[K, V, C]]
- SparkEnv.get.shuffleManager.getReader(dep.shuffleHandle, split.index, split.index + 1, context)
- .read()
- .asInstanceOf[Iterator[(K, C)]]
- }
下面是HashShuffleReader的read():
- /** Read the combined key-values for this reduce task */
- override def read(): Iterator[Product2[K, C]] = {
- val ser = Serializer.getSerializer(dep.serializer)
- val iter = BlockStoreShuffleFetcher.fetch(handle.shuffleId, startPartition, context, ser)
- val aggregatedIter: Iterator[Product2[K, C]] = if (dep.aggregator.isDefined) {
- if (dep.mapSideCombine) {
- new InterruptibleIterator(context, dep.aggregator.get.combineCombinersByKey(iter, context))
- } else {
- new InterruptibleIterator(context, dep.aggregator.get.combineValuesByKey(iter, context))
- }
- } else {
- require(!dep.mapSideCombine, "Map-side combine without Aggregator specified!")
- // Convert the Product2s to pairs since this is what downstream RDDs currently expect
- iter.asInstanceOf[Iterator[Product2[K, C]]].map(pair => (pair._1, pair._2))
- }
- // Sort the output if there is a sort ordering defined.
- dep.keyOrdering match {
- case Some(keyOrd: Ordering[K]) =>
- // Create an ExternalSorter to sort the data. Note that if spark.shuffle.spill is disabled,
- // the ExternalSorter won't spill to disk.
- val sorter = new ExternalSorter[K, C, C](ordering = Some(keyOrd), serializer = Some(ser))
- sorter.insertAll(aggregatedIter)
- context.taskMetrics.incMemoryBytesSpilled(sorter.memoryBytesSpilled)
- context.taskMetrics.incDiskBytesSpilled(sorter.diskBytesSpilled)
- sorter.iterator
- case None =>
- aggregatedIter
- }
- }
spark shuffle 相关细节整理的更多相关文章
- Chrome浏览器相关细节整理
一.上传文件卡死 可能时由于输入法的原因导致上传文件浏览器卡死.将输入法改为英文模式再操作上传文件就不会卡死了.
- Spark Shuffle的技术演进
在Spark或Hadoop MapReduce的分布式计算框架中,数据被按照key分成一块一块的分区,打散分布在集群中各个节点的物理存储或内存空间中,每个计算任务一次处理一个分区,但map端和re ...
- Spark 性能相关参数配置详解-shuffle篇
随着Spark的逐渐成熟完善, 越来越多的可配置参数被添加到Spark中来, 在Spark的官方文档http://spark.apache.org/docs/latest/configuration. ...
- Spark 性能相关參数配置具体解释-shuffle篇
作者:刘旭晖 Raymond 转载请注明出处 Email:colorant at 163.com BLOG:http://blog.csdn.net/colorant/ 随着Spark的逐渐成熟完好, ...
- spark shuffle:分区原理及相关的疑问
一.分区原理 1.为什么要分区?(这个借用别人的一段话来阐述.) 为了减少网络传输,需要增加cpu计算负载.数据分区,在分布式集群里,网络通信的代价很大,减少网络传输可以极大提升性能.mapreduc ...
- spark shuffle写操作之SortShuffleWriter
提出问题 1. spark shuffle的预聚合操作是如何做的,其中底层的数据结构是什么?在数据写入到内存中有预聚合,在读溢出文件合并到最终的文件时是否也有预聚合操作? 2. shuffle数据的排 ...
- Spark面试相关
Spark Core面试篇01 随着Spark技术在企业中应用越来越广泛,Spark成为大数据开发必须掌握的技能.前期分享了很多关于Spark的学习视频和文章,为了进一步巩固和掌握Spark,在原有s ...
- Spark Shuffle(一)ShuffleWrite:Executor如何将Shuffle的结果进行归并写到数据文件中去(转载)
转载自:https://blog.csdn.net/raintungli/article/details/70807376 当Executor进行reduce运算的时候,生成运算结果的临时Shuffl ...
- Spark 性能相关参数配置详解-任务调度篇
随着Spark的逐渐成熟完善, 越来越多的可配置参数被添加到Spark中来, 本文试图通过阐述这其中部分参数的工作原理和配置思路, 和大家一起探讨一下如何根据实际场合对Spark进行配置优化. 由于篇 ...
随机推荐
- Android6.0权限组申请
void checkPermission() { final List<String> permissionsList = new ArrayList<>(); if (Bui ...
- 保存emoji到数据库
/// <summary> /// <summary> /// 字符串转Unicode /// </summary> /// <param name=&quo ...
- Erlang C1500K长连接推送服务-性能
Whatsapp已经使用Erlang在生产环境跑到96GB内存单机 3M长连接,参加:WhatsApp的Erlang世界.毕竟业务级别能达到Whatsapp那样极少,现在只有千万级,单机太多挂一台影响 ...
- Redis 3.0 Cluster集群配置
Redis 3.0 Cluster集群配置 安装环境依赖 安装gcc:yum install gcc 安装zlib:yum install zib 安装ruby:yum install ruby 安装 ...
- Splinter学习——不仅仅是自动化测试哦
前两天,想抢购一个小米MIX,结果,一开始抢就没有了.于是想,作为程序猿,总得有点特殊手段吧,比如说一个小脚本.最近在学习python,百度了一下,发现了Splinter这个强大的东东!用了不到两小时 ...
- Redis之清除所有缓存
方法: /// <summary> /// 清除redis所有缓存 /// </summary> /// <param name="redisUrl" ...
- xml之dom4j解析
* 使用dom4j解析xml 实例在java520里面 TextDom4j * dom4j,是一个组织,针对xml解析,提供解析器dom4j * dom4j不是javase的一部分,想要使用第一步需要 ...
- python关于分割与拼接的那些事
1.split分割 基于re模块和正则表达式对象的方法split(),以后再做学习 基于字符串的split()方法 :字符串对象的split()方法也只能处理非常简单的情况,而且不支持多个分隔符,对分 ...
- 《Scalable IO in Java》笔记
Scalable IO in Java http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf 基本上所有的网络处理程序都有以下基本的处理过程:Read reque ...
- JS实现常用排序算法—经典的轮子值得再造
关于排序算法的博客何止千千万了,也不多一个轮子,那我就斗胆粗制滥造个轮子吧!下面的排序算法未作说明默认是从小到大排序. 1.快速排序2.归并排序3.冒泡排序4.选择排序(简单选择排序)5.插入排序(直 ...