Spark Shuffle阶段写磁盘为何仍属内存计算方案?
Spark Shuffle阶段落盘,为什么还是比Hadoop MapReduce快?
首先先破个常见的误解:“Spark所有中间结果全放内存不落盘”本来就是早期宣传里被简化过头的说法,两者的性能差距从来不是“写不写磁盘”,而是IO的成本、次数、冗余度从根上就不一样。
- 落盘的位置和冗余成本天差地别
MapReduce的中间结果是必须写HDFS这类分布式文件系统的:写的时候要走网络传输存多副本、做数据校验、更新分布式元数据,读的时候大概率还要跨网络从其他节点拉数据,一次Shuffle的网络+磁盘IO是好几份的冗余开销。而Spark的Shuffle文件是直接写在执行节点本地的机器磁盘上,没有多副本要求,没有分布式存储的额外流程开销,下游Stage的任务要是刚好调度到存了对应Shuffle数据的节点,直接读本地文件就行,连网络传输都省了。 - 落盘的次数差了好几个量级
MapReduce是固定的Map-Shuffle-Reduce两阶段模型,只要是多步骤的计算(比如两次Join加一次聚合),就得拆成好几个MR作业串行跑,每个作业的Map输出、Reduce输出都要落一次HDFS,下一个作业再从HDFS读回来,计算步骤越多,重复读写磁盘的次数就越多。而Spark是基于DAG调度的,只有碰到宽依赖(也就是Shuffle边界)才会落一次本地盘,同一个Stage里所有连续的窄依赖操作(比如map、filter、mapPartitions这些)全是流水线式在内存里跑完,根本不会额外落盘,普通的数仓任务Spark的落盘次数可能只有MR的1/3甚至更少。 - Shuffle本身的无用开销少很多
MapReduce的Shuffle逻辑是写死的,不管你的业务需不需要数据排序,Map端溢写、Reduce端拉取后都会强制做全量排序,很多不需要排序的场景(比如简单的分区聚合、Hash Join)白白浪费大量CPU和IO在排序上。Spark的Shuffle是可配置、可优化的:不需要排序的场景可以直接跳过排序流程按分区写文件,后续版本还陆续加了小文件合并、堆外内存管理、零拷贝拉取等优化,随机IO、序列化开销都比原生MR低不少。 - 内存计算的优势根本不在Shuffle阶段
大家说的Spark内存缓存,本来就不是用来缓存Shuffle数据的,而是用来存那些需要反复用到的热数据——比如迭代机器学习任务里的中间结果、反复关联的维度表,这类数据用MR跑的话每次都要重新从HDFS读、重新计算,Spark缓存到内存之后直接就能用,这部分的性能提升是MR完全不具备的,和Shuffle落盘的逻辑根本不冲突。
说白了,Spark就算Shuffle写磁盘,写的也是本地盘、只写一次、没有冗余副本、没有多余的排序开销,再加上Stage内流水线计算、热数据缓存的优势,比MR快是很正常的。真要是碰到内存完全装不下、Shuffle数据量极大的场景,Spark也会溢写磁盘,性能优势会缩小,但依然比MR的冗余IO效率高。
内容的提问来源于stack exchange,提问作者krezno
相关产品推荐
相关产品推荐

