Spark Partition

partition是spark rdd計算的最小單元。爲什麼是最小單元?先從分佈式說起,分佈式計算的特點就是批處理,將大量的數據分成若干批次,使得利用廉價機器搭建的集羣也可以完成海量數據的計算。大量的數據分散在集羣中的若干節點上,每個節點上的那部分數據,在執行計算的時候,又可以切分成若干份,每一份就是一個批次,也就是一個partition。

spark計算的性能與partition的數量有很大的關係。每個task處理一個partition。如果partition的數量小於集羣可用的CPU核數,就不能充分利用並行計算的性能,還可能引發數據傾斜的問題。但是partition的數量遠大於集羣可用CPU核數的時候,又可能導致資源分配的時間超過任務實際執行的時間。那麼partition的數量又是由什麼決定的呢?

備註: 這裏提到的相關API及代碼都是基於PySpark

一、數據源

rdd如果是從某些數據源創建的,則partition的數量與這些數據源有直接關係。下文的sc指SparkContext實例, spark指的是SparkSession實例。

從內存中創建

sc.parallelize(...)

這種方式創建的partition數量等於SparkContext的defaultParallelism的值,或者等於傳給parallelize的numSlices參數。

讀取HDFS文件創建

包括使用sc.textFile(...)spark.sql(...)查詢Hive表得到的結果的partition數量等於HDFS中的文件塊數。

二、通過計算

通過Generic Transformation創建

定義

rdd_size = rdd.getNumPartitions()
other_rdd_size = rdd.getNumPartitions()
  1. filter(), map(), flatMap(), distinct()partition數量等於parent RDD的數量。
  2. rdd.union(other_rdd)partition數量等於rdd_size + other_rdd_size
  3. rdd.intersection(other_rdd) partition數量等於max(rdd_size, other_rdd_size)
  4. rdd.subtract(other_rdd) partition數量等於rdd_size
  5. rdd.cartesian(other_rdd) partition數量等於rdd_size * other_rdd_size

通過Key-based Transformation創建

什麼是key-based?key-based指的是執行transformation的rdd都必須是pair rdd,在pyspark中,並沒有pair rdd這個類型,一個rdd要成爲有效的pair rdd, 只需要rdd中的每條記錄滿足k, v = kv,也就是只要是一個支持迭代,且一共含有兩個元素的對象即可,可以是(k, v) ,也可以是[k, v], 也可以是自定義類型等。

在默認的情況下:

  1. reduceByKey(), foldByKey(), combineByKey(), groupByKey() partition數量等於parent rdd的數量, 使用HashPartitioner,允許自己指定hash函數
  2. sortByKey() partition數量等於parent rdd的數量,使用RangePartitioner
  3. mapValues(), flatMapValues() partition數量等於parent rdd的數量,使用parent rdd的partitioner,這兩個函數接收的parent rdd必須爲pair rdd, 確保執行完以後保持原有partition不變
  4. join(), leftOuterJoin(), rightOuterJoin() partition數量取決於相關的兩個rdd, 使用HashPartitioner

三、自定義partition數量

在使用HashPartitioner的transformation的函數中,可以通過numPartitions與partitionFunc改變默認的partition數量以及數據的分佈方式。

numPartitions指定結果rdd的partition數量, partitionFunc只要是一個返回一個int數值的函數即可,它決定了一條記錄將要被分配的partition的index

# pyspark 源碼
buckets[partitionFunc(k) % numPartitions].append((k, v))

幾個幫助瞭解partition的API

# 查看partition數量
rdd.getNumPartitions()

# 查看所用partitioner
rdd.partitioner 

# 查看具體數據在每個partition的分佈
rdd.glom().collect()  # 僅適用小量數據, 數據量大慎用

在做試驗的時候, 通過rdd.glom().collect()可以看到數據在每個partition的分佈情況,有的時候可以看出, 當partition數量多於數據條數,或是數據傾斜的時候,有的partition是空的,所以spark不會因爲某個partition是空的就移除它,而每個partition又對應一個task,爲空的partition啓停任務, 必然引起不必要的資源消耗而影響性能。 所以分配得當的partition數量是非常重要的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章