Làm thế nào các giai đoạn được chia thành các nhiệm vụ trong Spark?


143

Giả sử như sau rằng chỉ có một công việc Spark đang chạy tại mọi thời điểm.

Những gì tôi nhận được cho đến nay

Đây là những gì tôi hiểu những gì xảy ra trong Spark:

  1. Khi a SparkContextđược tạo, mỗi nút worker bắt đầu một tệp thực thi. Các bộ điều hành là các quy trình riêng biệt (JVM), kết nối lại với chương trình trình điều khiển. Mỗi người thực hiện có các chương trình điều khiển. Thoát khỏi một trình điều khiển, tắt các giám đốc điều hành. Mỗi người thực thi có thể giữ một số phân vùng.
  2. Khi một công việc được thực thi, một kế hoạch thực hiện được tạo ra theo biểu đồ dòng.
  3. Công việc thực thi được chia thành các giai đoạn, trong đó các giai đoạn chứa nhiều biến đổi và hành động lân cận (trong biểu đồ dòng dõi), nhưng không có sự xáo trộn. Do đó các giai đoạn được phân tách bằng xáo trộn.

hình 1

tôi hiểu điều đó

  • Một tác vụ là một lệnh được gửi từ trình điều khiển đến một người thực thi bằng cách tuần tự hóa đối tượng Hàm.
  • Người thực thi giải tuần tự hóa (với trình điều khiển jar) lệnh (tác vụ) và thực thi nó trên một phân vùng.

nhưng

Câu hỏi

Làm thế nào để tôi chia giai đoạn thành các nhiệm vụ?

Đặc biệt:

  1. Là các nhiệm vụ được xác định bởi các biến đổi và hành động hoặc có thể là nhiều biến đổi / hành động trong một nhiệm vụ?
  2. Là các nhiệm vụ được xác định bởi phân vùng (ví dụ: một nhiệm vụ cho mỗi giai đoạn trên mỗi phân vùng).
  3. Các tác vụ được xác định bởi các nút (ví dụ: một tác vụ trên mỗi giai đoạn trên mỗi nút)?

Những gì tôi nghĩ (chỉ trả lời một phần, ngay cả khi đúng)

Trong https://0x0fff.com/spark-arch architecture-shuffle, shuffle được giải thích với hình ảnh

nhập mô tả hình ảnh ở đây

và tôi có ấn tượng rằng quy tắc là

mỗi giai đoạn được chia thành các nhiệm vụ # số phân vùng, không liên quan đến số lượng nút

Đối với hình ảnh đầu tiên của tôi, tôi muốn nói rằng tôi có 3 nhiệm vụ bản đồ và 3 nhiệm vụ giảm.

Đối với hình ảnh từ 0x0fff, tôi muốn nói có 8 tác vụ bản đồ và 3 tác vụ giảm (giả sử rằng chỉ có ba tệp màu cam và ba màu xanh đậm).

Câu hỏi mở trong mọi trường hợp

Đúng không? Nhưng ngay cả khi điều đó là chính xác, các câu hỏi của tôi ở trên vẫn chưa được trả lời, bởi vì nó vẫn mở, cho dù nhiều thao tác (ví dụ như nhiều bản đồ) nằm trong một nhiệm vụ hoặc được tách thành một nhiệm vụ cho mỗi hoạt động.

Những gì người khác nói

Nhiệm vụ trong Spark là gì? Làm thế nào để công nhân Spark thực hiện các tập tin jar? và Trình lập lịch biểu Apache Spark chia các tệp thành các tác vụ như thế nào? tương tự nhau, nhưng tôi không cảm thấy rằng câu hỏi của tôi đã được trả lời rõ ràng ở đó.

Câu trả lời:


52

Bạn có một phác thảo khá đẹp ở đây. Để trả lời câu hỏi của bạn

  • Một riêng biệt task không cần phải được đưa ra cho mỗi phân vùng dữ liệu cho mỗi stage. Hãy xem xét rằng mỗi phân vùng có thể sẽ nằm trên các vị trí vật lý riêng biệt - ví dụ: các khối trong HDFS hoặc thư mục / khối lượng cho một hệ thống tệp cục bộ.

Lưu ý rằng việc gửi Stages được điều khiển bởi DAG Scheduler. Điều này có nghĩa là các giai đoạn không phụ thuộc lẫn nhau có thể được gửi tới cụm để thực hiện song song: điều này tối đa hóa khả năng song song hóa trên cụm. Vì vậy, nếu các hoạt động trong dataflow của chúng tôi có thể xảy ra đồng thời, chúng tôi sẽ thấy nhiều giai đoạn được khởi chạy.

Chúng ta có thể thấy rằng trong hành động trong ví dụ đồ chơi sau đây, trong đó chúng ta thực hiện các loại hoạt động sau:

  • tải hai nguồn dữ liệu
  • thực hiện một số thao tác bản đồ trên cả hai nguồn dữ liệu riêng biệt
  • tham gia cùng họ
  • thực hiện một số thao tác bản đồ và bộ lọc trên kết quả
  • lưu kết quả

Vậy sau đó chúng ta sẽ có bao nhiêu giai đoạn?

  • Mỗi giai đoạn 1 để tải hai nguồn dữ liệu song song = 2 giai đoạn
  • Giai đoạn thứ ba đại diện cho joinđiều đó phụ thuộc vào hai giai đoạn còn lại
  • Lưu ý: tất cả các hoạt động tiếp theo làm việc trên dữ liệu đã tham gia có thể được thực hiện trong cùng một giai đoạn vì chúng phải diễn ra tuần tự. Không có lợi ích gì khi khởi chạy các giai đoạn bổ sung vì chúng không thể bắt đầu công việc cho đến khi hoàn thành thao tác trước đó.

Đây là chương trình đồ chơi

val sfi  = sc.textFile("/data/blah/input").map{ x => val xi = x.toInt; (xi,xi*xi) }
val sp = sc.parallelize{ (0 until 1000).map{ x => (x,x * x+1) }}
val spj = sfi.join(sp)
val sm = spj.mapPartitions{ iter => iter.map{ case (k,(v1,v2)) => (k, v1+v2) }}
val sf = sm.filter{ case (k,v) => v % 10 == 0 }
sf.saveAsTextFile("/data/blah/out")

Và đây là DAG của kết quả

nhập mô tả hình ảnh ở đây

Bây giờ: có bao nhiêu nhiệm vụ ? Số lượng nhiệm vụ phải bằng

Tổng của ( Stage* #Partitions in the stage)


2
Cảm ơn! Vui lòng giải thích câu trả lời của bạn liên quan đến văn bản của tôi: 1) Định nghĩa của tôi về các giai đoạn không toàn diện? Có vẻ như tôi đã bỏ lỡ yêu cầu rằng một giai đoạn không thể chứa các hoạt động có thể song song. Hoặc là mô tả của tôi ngụ ý đúng điều đó đã? 2) Số lượng tác vụ phải thực hiện cho công việc được xác định bởi số lượng phân vùng, nhưng không phải số lượng bộ xử lý hoặc nút, trong khi số lượng tác vụ có thể được thực thi cùng lúc phụ thuộc vào số lượng bộ xử lý, phải không? 3) Một tác vụ có thể chứa nhiều hoạt động?
Make42

1
4) Ý của bạn là gì với câu cuối cùng của bạn? Rốt cuộc, các phân vùng số có thể thay đổi từ giai đoạn này sang giai đoạn khác. Ý bạn là đây là cách bạn định cấu hình công việc cho tất cả các giai đoạn?
Make42

@ Make42 Tất nhiên số lượng phân vùng có thể thay đổi theo từng giai đoạn - bạn đã đúng. Đó là ý định của tôi bằng cách nói sum(..)để tính đến sự thay đổi đó.
javadba

wow, câu trả lời của bạn hoàn toàn ổn nhưng thật không may, câu cuối cùng chắc chắn là một khái niệm sai. Điều đó không có nghĩa là số phân vùng trong một giai đoạn bằng số lượng bộ xử lý, tuy nhiên, bạn có thể đặt số lượng phân vùng cho RDD theo số lượng lõi được trình bày trên máy của bạn.
epcpu

@epcpu Đó là một trường hợp đặc biệt - nhưng tôi đồng ý rằng nó sẽ gây hiểu lầm vì vậy tôi đang gỡ bỏ nó.
javadba

26

Điều này có thể giúp bạn hiểu rõ hơn về các phần khác nhau:

  • Giai đoạn: là tập hợp các nhiệm vụ. Cùng một quá trình chạy với các tập hợp con khác nhau của dữ liệu (phân vùng).
  • Nhiệm vụ: đại diện cho một đơn vị công việc trên một phân vùng của một tập dữ liệu phân tán. Vì vậy, trong mỗi giai đoạn, number-of-task = number-of-phân vùng, hoặc như bạn đã nói "một nhiệm vụ trên mỗi giai đoạn trên mỗi phân vùng.
  • Mỗi bộ thực thi chạy trên một thùng chứa sợi và mỗi thùng chứa nằm trên một nút.
  • Mỗi giai đoạn sử dụng nhiều người thực thi, mỗi người thực thi được phân bổ nhiều vcores.
  • Mỗi vcore có thể thực hiện chính xác một nhiệm vụ tại một thời điểm
  • Vì vậy, ở bất kỳ giai đoạn nào, nhiều nhiệm vụ có thể được thực hiện song song. số lượng tác vụ đang chạy = số lượng vcores đang được sử dụng.

2
Đây là một bài đọc thực sự hữu ích về kiến ​​trúc tia lửa: 0x0fff.com/spark-arch architecture
pedram bashiri

Tôi không nhận được số điểm của bạn 3. Theo như tôi biết, mỗi nút có thể có nhiều người thi hành, vì vậy theo điểm 3: Chỉ nên có một người thực thi cho mỗi nút. Bạn có thể làm rõ điểm này?
Rituparno Behera

@RituparnoBehera mỗi nút có thể có các thùng chứa đa dạng và do đó nhiều bộ thực thi Spark. Kiểm tra liên kết này. docs.cloudera.com/racer/7.0.2/rucky-spark-appluggest/ trên
pedram bashiri

15

Nếu tôi hiểu chính xác, có 2 điều (liên quan) làm bạn bối rối:

1) Điều gì quyết định nội dung của một nhiệm vụ?

2) Điều gì quyết định số lượng tác vụ sẽ được thực hiện?

Động cơ của Spark "kết dính" các hoạt động đơn giản trên các bánh liên tiếp, ví dụ:

rdd1 = sc.textFile( ... )
rdd2 = rdd1.filter( ... )
rdd3 = rdd2.map( ... )
rdd3RowCount = rdd3.count

Vì vậy, khi rdd3 được tính toán (một cách lười biếng), spark sẽ tạo ra một tác vụ trên mỗi phân vùng của rdd1 và mỗi tác vụ sẽ thực thi cả bộ lọc và bản đồ trên mỗi dòng để tạo ra rdd3.

Số lượng tác vụ được xác định bởi số lượng phân vùng. Mỗi RDD có một số phân vùng xác định. Đối với RDD nguồn được đọc từ HDFS (ví dụ sử dụng sc.textFile (...)), số lượng phân vùng là số lượng phân tách được tạo bởi định dạng đầu vào. Một số thao tác trên RDD (s) có thể dẫn đến RDD với số lượng phân vùng khác nhau:

rdd2 = rdd1.repartition( 1000 ) will result in rdd2 having 1000 partitions ( regardless of how many partitions rdd1 had ).

Một ví dụ khác là tham gia:

rdd3 = rdd1.join( rdd2  , numPartitions = 1000 ) will result in rdd3 having 1000 partitions ( regardless of partitions number of rdd1 and rdd2 ).

(Hầu hết) các hoạt động thay đổi số lượng phân vùng liên quan đến xáo trộn, ví dụ: Khi chúng tôi thực hiện:

rdd2 = rdd1.repartition( 1000 ) 

Điều thực sự xảy ra là nhiệm vụ trên mỗi phân vùng của rdd1 cần tạo ra đầu ra cuối có thể được đọc bởi giai đoạn sau để làm cho rdd2 có chính xác 1000 phân vùng (Cách chúng thực hiện? Hash hoặc Sắp xếp ). Nhiệm vụ ở bên này đôi khi được gọi là "Nhiệm vụ bản đồ (bên)". Một tác vụ sau này sẽ chạy trên rdd2 sẽ hoạt động trên một phân vùng (của rdd2!) Và sẽ phải tìm ra cách đọc / kết hợp các đầu ra phía bản đồ có liên quan đến phân vùng đó. Nhiệm vụ ở bên này đôi khi được gọi là "Nhiệm vụ giảm (bên)".

Hai câu hỏi có liên quan: số lượng tác vụ trong một giai đoạn là số lượng phân vùng (phổ biến cho các rdds liên tiếp "được dán" với nhau) và số lượng phân vùng của một rdd có thể thay đổi giữa các giai đoạn (bằng cách chỉ định số lượng phân vùng cho một số xáo trộn gây ra hoạt động chẳng hạn).

Khi việc thực hiện giai đoạn bắt đầu, các tác vụ của nó có thể chiếm các vị trí nhiệm vụ. Số lượng vị trí tác vụ đồng thời là numExecutors * ExecutorCores. Nói chung, chúng có thể được chiếm bởi các nhiệm vụ từ các giai đoạn khác nhau, không phụ thuộc.

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.