Bug剖析篇-“Facebook 60TB+级的Apache Spark应用案例”

物联网

  因为证明了没有副作用,所以现在是没啥问题了。但是我个人认为其实还有一种办法是,取一个阈值,如果小于某个阈值则做double duplicate check,否则就直接加进去就好了。Spark 在很多地方也是这么做的。

  这里对于那些Task数特别大的朋友有福了。

  TimSort issue due to integer overflow for large buffer

  该Bug在1.6.2, 2.0.0 已经被解决。这个bug引起的问题现象初看起来会比较让人费解,大体如下:

物联网

  Snip20160906_21.png

  如图所示似乎违反了签名。其实问题本身确实比较复杂,通过提交了两个patch 才解决了该问题。

  一开始Facebook的哥们觉得应该是排序过程中内存的数据(比如ShuffleExternalSorter等Sorter) 超过8G 引起的,所以限制了数量,大于一定数量之后就进行spill操作。 后面一个新的PR应该是发现了问题的根源,在UnsafeSortDataFormat.copyRange() 和ShuffleSortDataFormat copyRange() 里,里面数组的偏移量是Integer类型,虽然数据集的大小不至于超过Int的最大值,但是在特定数据分布下且数据集>268.43 million 并则会触发这个Bug。我看了下,原先 Platform.copyMemory 签名本身也是Long的,但是实现copyRange的时候,默认传进去的是Int,所以产生了这个问题。大家瞅一眼代码就知道了。

物联网

  Snip20160906_23.png

  Fix Spark executor OOM

  该Bug 也是在1.6.2, 2.0.0 被修正。

  这个问题是这样的,Spark MemoryManager 可能认为还有10M内存,但是此时实际JVM可以提供给MemroyManager的内存只有5M了。所以分配内存的时候,就抛OOM了。这个时候应该捕获该OOM,并且保留已经申请到内存不归还,让MemoryManger 以为内存不够了,然后进行splill操作,从而凑足需要的内存。我们看TaskMemoryManager.allocatePage 方法。

物联网

  Snip20160906_25.png

  如果发生OOM了,则会捕获一次,,并且通过acquiredButNotUsed记住已经申请的量,最后再次调用allocatePage。这个时候allocatePage里的acquireExecutionMemory 方法可能发现自己内存不足了,就会发生spill了,从而释放出内存。

  其实这之前的代码也考虑过,但是没有在allocatePage的层次上做。这个Bug估计在单个Executor 并行运行Task数比较多的时候比较严重和容易发生的。

  Fix memory leak in the sorter SPARK-14363

  这个Bug 也是在1.6.2, 2.0.0被修正。

  在Spark排序中,指针和数据时分开存储的,进行spill操作其实是把数据替换到磁盘上。但是指针数组是必须在内存里。当数据被spill后,相应的,指向这些记录的指针其实也是要被释放的。数据量很大的时候,指针数组的大小也很可观。而且有一点值得指出的是,比如某个Executor 有五个Task并行运行,如果其中有三个完成了,那么可用内存增大,缓存到内存的数据就会变多,这个时候剩下的两个Task的指针数组也会增大,从而占用更多内存,接着新运行的三个Task可用内存变小了,从而失去了公平性。

  这些各个Sorter里都需要修正。

物联网

  Snip20160906_26.png

 

  红框部分便是释放指针数组的地方。里面会重新按初始initialSize值申请一块指针数组的内存。