Spark性能调优之道——解决Spark数据倾斜(Data Skew)的N种姿势 背景 测试方法论 结果

原文:http://blog.csdn.net/tanglizhe1105/article/details/51050974

很多使用Spark的朋友很想知道rdd里的元素是怎么存储的,它们占用多少存储空间?本次我们将以实验的方式进行测试,展示rdd存储开销性能。 
关于rdd的元素怎么存储,Spark里面实现了好几种不同类型的rdd,如最常见的MapPartitionsRDD,它处理map,filter,mapPartition等不引起shuffle的算子;再如ShuffledRDD它由shuffle操作生成的;像GraphX里面的VertexRDD、EdgeRDD和TripletRDD,它们是分区内构建了大量索引得rdd。不同的rdd拥有不同的元素存储机制,这些机制由rdd具体的分区对象来实现。关于rdd分区对象的存储方式,由于内容过多,这里不便介绍。

测试方法论

rdd到底占用多少空间,使用spark web ui的Executors查看是不够的,它只能显示executor目前已使用内存空间大小,并不能跟踪每个rdd空间使用情况。好在spark提供了cache功能,它能使我们手动控制rdd在内存中贮存。若另外一个rdd使用已cache的rdd,那么它的输入便是cached rdd,rdd的输入在web ui的job信息里是可以查看的。本实验的主要方法便是如此

val a = sc.parallelize( 1 to 1024*1024, 1).cache()
a.count()
val b = a.map( x=> (x, x)).cache()
b.count()
val c = b.map(x => x)
c.count()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

那么b.count()提交的job的输入能查看rdd a占用的内存空间大小,c.count()使得我们能查看rdd b占用的内存空间大小。

结果

元素数目 元素类型 rdd占用空间大小
1M Int 32MB
1M (Int, Int) 48MB
1M (Int, Int, Int) 120MB
1M Long 32MB
1M (Long, Long) 56MB
1M (Long, Long, Long) 120MB
1G Int 32GB
1G (Int, Int) 48GB
1G (Int, Int, Int) 120GB
1G Long 32GB
1G (Long, Long) 56GB
1G (Long, Long, Long) 120GB
10G Int 240GB
10G Long 246.7GB

本实验1M使用单个分区,1G使用80个分区(10个节点),10G使用144个分区(18个节点)

1M与1G元素规模的结果吻合的太好了,以至于我都有不敢相信,可是测试出来的结果就是这样的,这也证明spark在数据规模可扩展性方面真是太完美了。 
关于每条元素的存储开销,若元素是Java对象存储,那么每条元素至少会带入18自己额外开销,若以基本数据类型存储,则不会带入额外开销。 
测试结果有一些诡异的地方: 
相同元素规模情况下,Int与Long占用空间相同,(Int, Int)与(Long, Long)不同,但(Int, Int, Int)与(Long, Long, Long)又相同。 
1M Int净存储空间为4MB,但占用32MB空间,且占用空间一般呈整数样式。