Apache Spark: map vs mapPartitions?


133

Sự khác biệt giữa RDD mapmapPartitionsphương pháp là gì? Và flatMapcư xử như thế nào maphay thích mapPartitions? Cảm ơn.

(chỉnh sửa) tức là sự khác biệt (về mặt ngữ nghĩa hoặc về mặt thực thi) giữa

  def map[A, B](rdd: RDD[A], fn: (A => B))
               (implicit a: Manifest[A], b: Manifest[B]): RDD[B] = {
    rdd.mapPartitions({ iter: Iterator[A] => for (i <- iter) yield fn(i) },
      preservesPartitioning = true)
  }

Và:

  def map[A, B](rdd: RDD[A], fn: (A => B))
               (implicit a: Manifest[A], b: Manifest[B]): RDD[B] = {
    rdd.map(fn)
  }

3
Sau khi đọc câu trả lời dưới đây, bạn có thể xem [trải nghiệm này] được chia sẻ bởi một người thực sự sử dụng nó. ( Bzhangusc.wordpress.com/2014/06/19/ nam ) bzhangusc.wordpress.com/2014/06/19 /
Lọ

Câu trả lời:


121

Sự khác biệt giữa bản đồ của RDD và phương thức mapPartitions là gì?

Bản đồ phương thức chuyển đổi từng phần tử của RDD nguồn thành một phần tử duy nhất của RDD kết quả bằng cách áp dụng một hàm. mapPartitions chuyển đổi từng phân vùng của RDD nguồn thành nhiều phần tử của kết quả (có thể không có).

Và FlatMap có hoạt động như map hay like mapPartitions không?

Không, FlatMap hoạt động trên một phần tử (as map) và tạo ra nhiều phần tử của kết quả (as mapPartitions).


3
Cảm ơn - vì vậy bản đồ có gây xáo trộn (hoặc thay đổi số lượng phân vùng) không? Nó di chuyển dữ liệu giữa các nút? Tôi đã sử dụng mapPartitions để tránh di chuyển dữ liệu giữa các nút, nhưng không chắc chắn nếu flapMap sẽ làm như vậy.
Nicholas White

Nếu bạn xem nguồn - github.com/apache/incubator-spark/blob/ , và github.com/apache/incubator-spark/blob/iêu - cả hai mapflatMapcó cùng phân vùng chính là cha mẹ.
Alexey Romanov

13
Lưu ý, một bài thuyết trình được cung cấp bởi một diễn giả tại Hội nghị thượng đỉnh San Francisco 2013 (goo.gl/JZXDCR) nhấn mạnh rằng các tác vụ có chi phí trên mỗi bản ghi cao sẽ hoạt động tốt hơn với mapPartition so với chuyển đổi bản đồ. Điều này là, theo bài thuyết trình, do chi phí cao để thiết lập một nhiệm vụ mới.
Mikel Urkia

1
Tôi đang thấy điều ngược lại - ngay cả với các thao tác rất nhỏ, việc gọi mapPartitions và lặp lại nhanh hơn so với gọi bản đồ. Tôi giả định rằng đây chỉ là chi phí khởi động công cụ ngôn ngữ sẽ xử lý tác vụ bản đồ. (Tôi đang ở R, có thể có nhiều chi phí khởi động hơn.) Nếu bạn sẽ thực hiện nhiều thao tác, thì mapPartitions dường như nhanh hơn một chút - Tôi cho rằng điều này là do nó chỉ đọc RDD một lần. Ngay cả khi RDD được lưu trong bộ nhớ cache trong RAM, điều đó giúp tiết kiệm rất nhiều chi phí từ chuyển đổi loại.
Bob

3
mapvề cơ bản có chức năng của bạn f, và chuyển nó vào iter.map(f). Vì vậy, về cơ bản nó là một phương pháp thuận tiện mà kết thúc tốt đẹp mapPartitions. Tôi sẽ ngạc nhiên nếu có một lợi thế về hiệu suất cho một công việc chuyển đổi kiểu bản đồ thuần túy (nghĩa là chức năng giống hệt nhau), nếu bạn cần tạo một số đối tượng để xử lý, nếu các đối tượng này có thể được chia sẻ thì mapPartitionssẽ thuận lợi.
NightWolf

129

Imp. TIỀN BOA :

Bất cứ khi nào bạn có khởi tạo hạng nặng nên được thực hiện một lần cho nhiều RDDphần tử thay vì một lần cho mỗi RDDphần tử và nếu việc khởi tạo này, chẳng hạn như tạo đối tượng từ thư viện bên thứ ba, không thể được tuần tự hóa (để Spark có thể truyền nó qua cụm các nút worker), sử dụng mapPartitions()thay cho map(). mapPartitions()cung cấp cho việc khởi tạo được thực hiện một lần trên mỗi tác vụ / luồng / phân vùng worker thay vì một lần cho mỗi RDDphần tử dữ liệu : xem bên dưới.

val newRd = myRdd.mapPartitions(partition => {
  val connection = new DbConnection /*creates a db connection per partition*/

  val newPartition = partition.map(record => {
    readMatchingFromDB(record, connection)
  }).toList // consumes the iterator, thus calls readMatchingFromDB 

  connection.close() // close dbconnection here
  newPartition.iterator // create a new iterator
})

Quý 2 không flatMaphành xử như bản đồ hay như thế mapPartitionsnào?

Đúng. vui lòng xem ví dụ 2 của flatmap.. nó tự giải thích.

Q1. Sự khác biệt giữa RDD mapmapPartitions

maphoạt động chức năng đang được sử dụng ở cấp độ phần tử trong khi mapPartitionsthực hiện chức năng ở cấp độ phân vùng.

Kịch bản ví dụ : nếu chúng ta có 100K phần tử trong mộtRDDphân vùngcụ thểthì chúng ta sẽ tắt chức năng đang được sử dụng bởi phép chuyển đổi ánh xạ 100K lần khi chúng ta sử dụngmap.

Ngược lại, nếu chúng ta sử dụng mapPartitionsthì chúng ta sẽ chỉ gọi hàm cụ thể một lần, nhưng chúng ta sẽ chuyển trong tất cả các bản ghi 100K và nhận lại tất cả các phản hồi trong một lệnh gọi hàm.

Sẽ có hiệu suất tăng do maphoạt động trên một chức năng cụ thể rất nhiều lần, đặc biệt là nếu chức năng đó đang làm một việc đắt tiền mỗi lần mà nó sẽ không cần phải làm nếu chúng ta chuyển tất cả các yếu tố cùng một lúc (trong trường hợp mappartitions).

bản đồ

Áp dụng một hàm biến đổi trên mỗi mục của RDD và trả về kết quả dưới dạng RDD mới.

Các biến thể liệt kê

bản đồ def [U: ClassTag] (f: T => U): RDD [U]

Thí dụ :

val a = sc.parallelize(List("dog", "salmon", "salmon", "rat", "elephant"), 3)
 val b = a.map(_.length)
 val c = a.zip(b)
 c.collect
 res0: Array[(String, Int)] = Array((dog,3), (salmon,6), (salmon,6), (rat,3), (elephant,8)) 

mapPartitions

Đây là một bản đồ chuyên dụng chỉ được gọi một lần cho mỗi phân vùng. Toàn bộ nội dung của các phân vùng tương ứng có sẵn dưới dạng luồng giá trị tuần tự thông qua đối số đầu vào (Iterarator [T]). Hàm tùy chỉnh phải trả về một Iterator khác [U]. Các trình lặp kết quả kết hợp được tự động chuyển đổi thành RDD mới. Xin lưu ý rằng các bộ dữ liệu (3,4) và (6,7) bị thiếu trong kết quả sau do phân vùng chúng tôi đã chọn.

preservesPartitioningcho biết liệu hàm đầu vào có bảo vệ bộ phân vùng hay không, falsetrừ khi đây là một cặp RDD và hàm đầu vào không sửa đổi các khóa.

Các biến thể liệt kê

def mapPartitions [U: ClassTag] (f: Iterator [T] => Iterator [U], keepesPartitioning: Boolean = false): RDD [U]

ví dụ 1

val a = sc.parallelize(1 to 9, 3)
 def myfunc[T](iter: Iterator[T]) : Iterator[(T, T)] = {
   var res = List[(T, T)]()
   var pre = iter.next
   while (iter.hasNext)
   {
     val cur = iter.next;
     res .::= (pre, cur)
     pre = cur;
   }
   res.iterator
 }
 a.mapPartitions(myfunc).collect
 res0: Array[(Int, Int)] = Array((2,3), (1,2), (5,6), (4,5), (8,9), (7,8)) 

Ví dụ 2

val x = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8, 9,10), 3)
 def myfunc(iter: Iterator[Int]) : Iterator[Int] = {
   var res = List[Int]()
   while (iter.hasNext) {
     val cur = iter.next;
     res = res ::: List.fill(scala.util.Random.nextInt(10))(cur)
   }
   res.iterator
 }
 x.mapPartitions(myfunc).collect
 // some of the number are not outputted at all. This is because the random number generated for it is zero.
 res8: Array[Int] = Array(1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 7, 7, 7, 9, 9, 10) 

Chương trình trên cũng có thể được viết bằng cách sử dụng FlatMap như sau.

Ví dụ 2 sử dụng sơ đồ phẳng

val x  = sc.parallelize(1 to 10, 3)
 x.flatMap(List.fill(scala.util.Random.nextInt(10))(_)).collect

 res1: Array[Int] = Array(1, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10) 

Phần kết luận :

mapPartitionschuyển đổi nhanh hơn mapvì nó gọi hàm của bạn một lần / phân vùng, không phải một lần / phần tử ..

Đọc thêm: foreach Vs foreachPartitions Khi nào nên sử dụng Cái gì?


4
Tôi biết rằng bạn có thể sử dụng maphoặc mapPartitionsđể đạt được kết quả tương tự (xem hai ví dụ trong câu hỏi); câu hỏi này là về lý do tại sao bạn chọn cách này hơn cách khác. Các ý kiến ​​trong câu trả lời khác là thực sự hữu ích! Ngoài ra, bạn đã không đề cập đến điều đó mapflatMapchuyển falseđến preservesPartitioning, và ý nghĩa của việc đó là gì.
Nicholas White

2
hàm được thực thi mọi lúc so với hàm thực thi một lần cho phép ghép là liên kết tôi bị thiếu. Có quyền truy cập vào nhiều bản ghi dữ liệu cùng một lúc với mapPartition là một điều vô giá. đánh giá cao câu trả lời
Dấu chấm phẩy và băng keo

1
Có một kịch bản ở đâu maplà tốt hơn mapPartitions? Nếu mapPartitionstốt như vậy, tại sao nó không phải là triển khai bản đồ mặc định?
ru

1
@oneleggedmule: cả hai đều dành cho các yêu cầu khác nhau, chúng tôi phải sử dụng một cách khôn ngoan nếu bạn đang khởi tạo các tài nguyên như kết nối db (như trong ví dụ trên) rất tốn kém thì mappartitions là cách tiếp cận đúng vì một kết nối trên mỗi phân vùng. cũng saveAsTextFile mappartitions nội bộ sử dụng thấy
Ram Ghadiyaram

@oneleggedmule Theo quan điểm của tôi, map () dễ hiểu và dễ học hơn, và nó cũng là một phương pháp phổ biến của nhiều ngôn ngữ khác nhau. Có thể dễ sử dụng hơn so với mapPartitions () nếu ai đó không quen với phương pháp cụ thể Spark này lúc đầu. Nếu không có sự khác biệt về hiệu năng thì tôi thích sử dụng map ().
Raymond Chen

15

Bản đồ :

  1. Nó xử lý một hàng tại một thời điểm, rất giống với phương thức map () của MapReduce.
  2. Bạn trở về từ sự biến đổi sau mỗi hàng.

MapPartitions

  1. Nó xử lý phân vùng hoàn chỉnh trong một lần.
  2. Bạn chỉ có thể quay lại từ hàm sau khi xử lý toàn bộ phân vùng.
  3. Tất cả các kết quả trung gian cần phải được giữ trong bộ nhớ cho đến khi bạn xử lý toàn bộ phân vùng.
  4. Cung cấp cho bạn chức năng setup () map () và dọn dẹp () của MapReduce

Map Vs mapPartitions http://bytepadding.com/big-data/spark/spark-map-vs-mappartitions/

Spark Map http://bytepadding.com/big-data/spark/spark-map/

Spark mapPartitions http://bytepadding.com/big-data/spark/spark-mappartitions/


liên quan đến 2 - nếu bạn đang thực hiện các phép biến đổi iterator-to-iterator và không cụ thể hóa trình lặp thành một bộ sưu tập nào đó, thực tế, bạn sẽ không phải giữ toàn bộ phân vùng trong bộ nhớ, thực tế, theo cách đó, tia lửa sẽ có thể tràn các phần của phân vùng vào đĩa.
ilcord

4
Bạn không phải giữ toàn bộ phân vùng trong bộ nhớ, nhưng kết quả. Bạn không thể trả lại kết quả cho đến khi bạn đã xử lý toàn bộ phân vùng
KrazyGautam
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.