1. 程式人生 > >SparkCore(4)調優

SparkCore(4)調優

概述:Spark的調優主要有三個方面

 

1.Data Serialization  ****

(1)Java serialization:預設情況下,Spark使用Java的ObjectOutputStream框架序列化物件,並且可以使用您建立的任何實現Java .io. serializable類。還可以通過擴充套件java.io.Externalizable更緊密地控制序列化的效能。Java序列化是靈活的,但通常相當緩慢,並導致許多類的大型序列化格式。

package SparkReview.Core4

import org.apache.spark.storage.StorageLevel
import org.apache.spark.{SparkConf, SparkContext}

import scala.collection.mutable.ArrayBuffer
import scala.util.Random

object SerializaitonApp {
  def main(args: Array[String]): Unit = {
    val sparkConf=new SparkConf().setMaster("local[2]").setAppName("Serialization")
    val sc=new SparkContext(sparkConf)


    val names=Array[String]("G301","G302","G303")
    val ganders=Array[String]("male","famale")
    val address=Array[String]("beijing","shenzhen","hangzhou","yongkang")

    val infos=new ArrayBuffer[InFo]()//物件用來表示通用的、固定長度的原始二進位制資料緩衝區。
    // ArrayBuffer 不能直接操作,而是要通過型別陣列物件或 DataView 物件來操作,
    // 它們會將緩衝區中的資料表示為特定的格式,並通過這些格式來讀寫緩衝區的內容。

    for (i<-1 to 100000){
      val name=names(Random.nextInt(3))
      val gender=ganders(Random.nextInt(2))
      val addres=  address(Random.nextInt(4))
      infos+=InFo(name,gender,addres)
    }
    val rdd=sc.parallelize(infos)
   // rdd.persist(StorageLevel.MEMORY_ONLY)  //這個是34M
    rdd.persist(StorageLevel.MEMORY_ONLY_SER)   //這個是28僅僅只是小了一點,體現了java序列化的弊端
    rdd.count()
    sc.stop()
  }
  case class InFo(name: String,gender:String,address:String)


}

(2)Kryo serialization:Spark還可以使用Kryo庫(版本2)更快地序列化物件。與Java序列化相比,Kryo要快得多,緊湊得多(通常高達10x,這個有點誇張),但它不支援所有可序列化的型別,並且要求您預先註冊在程式中使用的類,以獲得最佳效能。

1)通過 spark-defaults.conf 新增 spark.serializer                 org.apache.spark.serializer.KryoSerializer

2)因為沒有註冊,所以它要去儲存你的所有類名和物件資訊,所以Memory變大了

2.Memory Tuning

在調優記憶體使用時有三個考慮因素:

物件使用的記憶體數量(您可能希望整個資料集適合於記憶體)、

訪問這些物件的成本以及

垃圾收集的開銷(如果物件的週轉率很高)。

預設情況下,Java物件的訪問速度很快,但與欄位中的原始資料相比,很容易消耗2-5倍的空間。這是由幾個原因造成的:每個不同的Java物件都有一個物件標頭,大約16個位元組,包含指向其類的指標之類的資訊。對於一個只有很少資料的物件(比如一個Int欄位),這可能比資料大。Java字串對原始字串資料的開銷約為40位元組(因為它們將其儲存在一個字元陣列中,並保留額外的資料,比如長度),並且由於字串內部使用UTF-16編碼,將每個字元儲存為兩個位元組。

因此,一個10個字元的字串可以很容易地消耗60個位元組。常用的集合類,如HashMap和LinkedList,使用連結資料結構,其中每個條目都有一個包裝器物件(例如Map.Entry)。這個物件不僅有一個頭,而且還有指向列表中下一個物件的指標(通常每個位元組8個位元組)。基元型別的集合通常將它們儲存為框形物件,比如java.lang.Integer。本節將首先概述Spark中的記憶體管理,然後討論使用者在應用程式中更有效地使用記憶體的具體策略。特別地,我們將描述如何確定物件的記憶體使用情況,以及如何通過更改資料結構或以序列化格式儲存資料來改進它。然後,我們將討論Spark快取大小的調優和Java垃圾收集器。

3.Memory Management Overview

4.Determining Memory Consumption

(1)作用:要確定資料集所需的記憶體消耗的大小,最好的方法是建立RDD,將其放入快取中,並檢視web UI中的“儲存”頁面。頁面將告訴您RDD佔用了多少記憶體。

(2)要估計特定物件的記憶體消耗,可以使用SizeEstimator的估計方法,這對於試驗不同的資料佈局來削減記憶體使用,以及確定廣播變數在每個執行程式堆上佔用的空間大小非常有用。

package SparkReview.Core4

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.util.SizeEstimator.estimate
object DeterminingMemory {
  def main(args: Array[String]): Unit = {
    val sparkConf=new SparkConf().setMaster("local[2]").setAppName("DeterMiningMemory")
    val sc=new SparkContext(sparkConf)
    val a=sc.textFile("E:\\若澤資料\\零基礎大資料篇第三期\\Hadoop綜合程式設計\\infos.txt")
//TODO...檢視資料的大小
println(estimate(a)) //(1)estimate檢視資料的大小,這對於確定廣播變數將佔用多少堆空間非常有用
    /**快取物件時每個執行程式或每個物件將佔用的空間
      *反序列化形式。這與序列化物件的大小不同,序列化物件的大小將不同
    *通常要小得多。*/
//(2)快取通過webUI的storage來看
    sc.stop()

  }

}

5.Level of Parallelism(並行度)

除非為每個操作設定足夠高的並行度,否則叢集不會得到充分利用。Spark會根據檔案的大小自動設定要在每個檔案上執行的map任務的數量(不過您可以通過SparkContext的可選引數來控制它)。對於分散式的reduce操作,如groupByKey和reduceByKey,它使用最大的父RDD分割槽數量。您可以將並行級別作為第二個引數傳遞(參見spark)。或設定config屬性spark.default.parallelism以更改預設值。通常,我們建議叢集中的每個CPU核心執行2-3個任務(防止task閒置)。

6.Memory Usage of Reduce Tasks

簡述:就是就多給點exectuor跑的快一點

7.Data Locality  ****

這就是對應RDD的五大特性之一的,最優位置去計算

Data locality is how close data is to the code processing it. There are several levels of locality based on the data’s current location. In order from closest to farthest:

  • PROCESS_LOCAL data is in the same JVM as the running code. This is the best locality possible
  • NODE_LOCAL data is on the same node. Examples might be in HDFS on the same node, or in another executor on the same node. This is a little slower than PROCESS_LOCAL because the data has to travel between processes
  • NO_PREF data is accessed equally quickly from anywhere and has no locality preference
  • RACK_LOCAL data is on the same rack of servers. Data is on a different server on the same rack so needs to be sent over the network, typically through a single switch
  • ANY data is elsewhere on the network and not in the same rack
    • 這是比較長了,預設3s時間長了,1s好點