Chà, hãy làm cho tập dữ liệu của bạn thú vị hơn một chút:
val rdd = sc.parallelize(for {
x <- 1 to 3
y <- 1 to 2
} yield (x, None), 8)
Chúng tôi có sáu yếu tố:
rdd.count
Long = 6
không có phân vùng:
rdd.partitioner
Option[org.apache.spark.Partitioner] = None
và tám phân vùng:
rdd.partitions.length
Int = 8
Bây giờ, hãy xác định trình trợ giúp nhỏ để đếm số phần tử trên mỗi phân vùng:
import org.apache.spark.rdd.RDD
def countByPartition(rdd: RDD[(Int, None.type)]) = {
rdd.mapPartitions(iter => Iterator(iter.length))
}
Vì chúng tôi không có trình phân vùng nên tập dữ liệu của chúng tôi được phân phối đồng nhất giữa các phân vùng ( Sơ đồ phân vùng mặc định trong Spark ):
countByPartition(rdd).collect()
Array[Int] = Array(0, 1, 1, 1, 0, 1, 1, 1)
Bây giờ hãy phân vùng lại tập dữ liệu của chúng tôi:
import org.apache.spark.HashPartitioner
val rddOneP = rdd.partitionBy(new HashPartitioner(1))
Vì tham số được truyền để HashPartitioner
xác định số lượng phân vùng, chúng tôi mong đợi một phân vùng:
rddOneP.partitions.length
Int = 1
Vì chúng ta chỉ có một phân vùng nên nó chứa tất cả các phần tử:
countByPartition(rddOneP).collect
Array[Int] = Array(6)
Lưu ý rằng thứ tự của các giá trị sau khi xáo trộn là không xác định.
Cùng một cách nếu chúng ta sử dụng HashPartitioner(2)
val rddTwoP = rdd.partitionBy(new HashPartitioner(2))
chúng ta sẽ nhận được 2 phân vùng:
rddTwoP.partitions.length
Int = 2
Vì rdd
được phân vùng theo dữ liệu chính sẽ không được phân phối thống nhất nữa:
countByPartition(rddTwoP).collect()
Array[Int] = Array(2, 4)
Bởi vì có ba phím và chỉ có hai giá trị khác nhau của hashCode
mod nên numPartitions
không có gì bất ngờ ở đây:
(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))
Chỉ để xác nhận những điều trên:
rddTwoP.mapPartitions(iter => Iterator(iter.map(_._1).toSet)).collect()
Array[scala.collection.immutable.Set[Int]] = Array(Set(2), Set(1, 3))
Cuối cùng, HashPartitioner(7)
chúng ta nhận được bảy phân vùng, ba phân vùng không trống với 2 phần tử mỗi phân vùng:
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)
Tóm tắt và ghi chú
HashPartitioner
lấy một đối số xác định số lượng phân vùng
giá trị được gán cho các phân vùng bằng cách sử dụng hash
các khóa. hash
chức năng có thể khác nhau tùy thuộc vào ngôn ngữ (Scala RDD có thể sử dụng hashCode
, DataSets
sử dụng MurmurHash 3, PySpark, portable_hash
).
Trong trường hợp đơn giản như thế này, khi khóa là một số nguyên nhỏ, bạn có thể giả định rằng đó hash
là một định danh ( i = hash(i)
).
API Scala sử dụng nonNegativeMod
để xác định phân vùng dựa trên hàm băm được tính toán,
nếu sự phân bổ của các khóa không đồng nhất, bạn có thể gặp phải tình huống khi một phần của cụm của bạn không hoạt động
các phím phải có thể băm. Bạn có thể kiểm tra câu trả lời của tôi cho Danh sách A làm chìa khóa cho ReduceByKey của PySpark để đọc về các vấn đề cụ thể của PySpark. Một vấn đề có thể xảy ra khác được làm nổi bật bởi tài liệu HashPartitioner :
Mảng Java có mã băm dựa trên danh tính của mảng chứ không phải nội dung của chúng, vì vậy việc cố gắng phân vùng RDD [Mảng [ ]] hoặc RDD [(Mảng [ ], _)] bằng HashPartitioner sẽ tạo ra kết quả không mong muốn hoặc không chính xác.
Trong Python 3, bạn phải đảm bảo rằng việc băm là nhất quán. Xem Ngoại lệ là gì: Tính ngẫu nhiên của hàm băm của chuỗi sẽ bị vô hiệu hóa thông qua PYTHONHASHSEED có nghĩa là trong pyspark?
Hash partitioner không phải là bị thương cũng không phải là khách quan. Nhiều khóa có thể được gán cho một phân vùng duy nhất và một số phân vùng có thể vẫn trống.
Xin lưu ý rằng các phương thức dựa trên băm hiện tại không hoạt động trong Scala khi kết hợp với các lớp trường hợp được xác định REPL ( Bình đẳng lớp trường hợp trong Apache Spark ).
HashPartitioner
(hoặc bất kỳ hình thức nào khác Partitioner
) xáo trộn dữ liệu. Trừ khi việc phân vùng được tái sử dụng giữa nhiều thao tác, nó không làm giảm lượng dữ liệu được xáo trộn.
(1, None)
vớihash(2) % P
nơi P là phân vùng. Có nên khônghash(1) % P
?