さて、あなたのデータセットをわずかにもっと面白くしましょう:
val rdd = sc.parallelize(for {
x <- 1 to 3
y <- 1 to 2
} yield (x, None), 8)
6つの要素があります。
rdd.count
Long = 6
パーティショナーなし:
rdd.partitioner
Option[org.apache.spark.Partitioner] = None
および8つのパーティション:
rdd.partitions.length
Int = 8
次に、パーティションごとの要素数をカウントする小さなヘルパーを定義しましょう。
import org.apache.spark.rdd.RDD
def countByPartition(rdd: RDD[(Int, None.type)]) = {
rdd.mapPartitions(iter => Iterator(iter.length))
}
パーティショナーがないため、データセットはパーティション間で均一に分散されます(Sparkのデフォルトのパーティショニングスキーム):
countByPartition(rdd).collect()
Array[Int] = Array(0, 1, 1, 1, 0, 1, 1, 1)
次に、データセットを再パーティション化します。
import org.apache.spark.HashPartitioner
val rddOneP = rdd.partitionBy(new HashPartitioner(1))
に渡されるパラメーターHashPartitioner
はパーティションの数を定義するため、1つのパーティションが必要です。
rddOneP.partitions.length
Int = 1
パーティションが1つしかないため、すべての要素が含まれています。
countByPartition(rddOneP).collect
Array[Int] = Array(6)
シャッフル後の値の順序は非決定論的であることに注意してください。
使用する場合も同じように HashPartitioner(2)
val rddTwoP = rdd.partitionBy(new HashPartitioner(2))
2つのパーティションを取得します。
rddTwoP.partitions.length
Int = 2
rdd
キーデータによってパーティション化されているため、均一に分散されなくなります。
countByPartition(rddTwoP).collect()
Array[Int] = Array(2, 4)
3つのキーがあり、hashCode
modの値が2つしかないため、numPartitions
ここでは予期しないことは何もありません。
(1 to 3).map((k: Int) => (k, k.hashCode, k.hashCode % 2))
scala.collection.immutable.IndexedSeq[(Int, Int, Int)] = Vector((1,1,1), (2,2,0), (3,3,1))
上記を確認するだけです:
rddTwoP.mapPartitions(iter => Iterator(iter.map(_._1).toSet)).collect()
Array[scala.collection.immutable.Set[Int]] = Array(Set(2), Set(1, 3))
最後に、HashPartitioner(7)
7つのパーティションを取得します。3つは空ではなく、それぞれ2つの要素があります。
val rddSevenP = rdd.partitionBy(new HashPartitioner(7))
rddSevenP.partitions.length
Int = 7
countByPartition(rddTenP).collect()
Array[Int] = Array(0, 2, 2, 2, 0, 0, 0)
まとめと注意事項
HashPartitioner
パーティションの数を定義する単一の引数を取ります
値はhash
、キーを使用してパーティションに割り当てられます。hash
機能は言語によって異なる場合があります(Scala RDDはhashCode
、DataSets
MurmurHash 3、PySparkを使用する場合がありますportable_hash
)。
キーが小さな整数であるこのような単純なケースでhash
は、それが単位元(i = hash(i)
)であると見なすことができます。
Scala APIはnonNegativeMod
、計算されたハッシュに基づいてパーティションを決定するために使用します。
キーの配布が均一でない場合、クラスターの一部がアイドル状態になる可能性があります
キーはハッシュ可能である必要があります。PySparkのreduceByKeyのキーとしてのAリストに対する私の回答を確認して、PySpark固有の問題について読むことができます。別の考えられる問題は、HashPartitionerのドキュメントで強調されています。
Java配列には、内容ではなく配列のIDに基づくhashCodeがあるため、HashPartitionerを使用してRDD [Array [ ]]またはRDD [(Array [ ]、_)]を分割しようとすると、予期しない結果または誤った結果が生成されます。
Python 3では、ハッシュが一貫していることを確認する必要があります。参照例外を何:文字列のハッシュのランダム性がPYTHONHASHSEEDを経由して無効にする必要がありpysparkに意味ですか?
ハッシュパーティショナーは単射でも全射でもありません。1つのパーティションに複数のキーを割り当てることができ、一部のパーティションは空のままにすることができます。
現在、ハッシュベースのメソッドは、REPLで定義されたケースクラス(Apache Sparkのケースクラスの同等性)と組み合わせると、Scalaでは機能しないことに注意してください。
HashPartitioner
(またはその他のPartitioner
)データをシャッフルします。パーティショニングが複数の操作間で再利用されない限り、シャッフルされるデータの量が減ることはありません。
(1, None)
てhash(2) % P
Pがパーティションです。そうではないhash(1) % P
ですか?