最近在開發spark streaming 程序時對這些概念有了深刻的理解。在此總結下。
我最近的 spark streaming 核心代碼如下:
stream.foreachRDD(rdd -> { try { // extract all activity events rdd.flatMap(record -> { String topic = record.topic(); TopicHandler handler = HandlerFactory.getHandler(topic); return handler.handle(record.value()); }).groupBy(act -> act.getMemberSrl()) // process all activities .foreachPartition(itr -> { while (itr.hasNext()) { Iterable<Activity> acts = itr.next()._2; Processor.process(acts); } }); } catch (Exception e) { log.error("consumer rdd error", e); } });
1 Job
就是spark batch/streaming 裏的一系列的數據轉換 + 一個結果算子(比如collect, count,foreachPartition )。
2 Stage
Stage概念是spark中獨有的。一般而言一個Job會切換成一定數量的stage。各個stage之間按照順序執行。至於stage是怎麼切分的,首選得知道spark論文中提到的narrow dependency(窄依賴)和wide dependency( 寬依賴)的概念。其實很好區分,看一下父RDD中的數據是否進入不同的子RDD,如果只進入到一個子RDD則是窄依賴,否則就是寬依賴。寬依賴和窄依賴的邊界就是stage的劃分點
參考:https://blog.csdn.net/u013013024/article/details/72876427
在我的spark 程序中,groupBy 和 foreachPartition 因爲涉及到shuffle操作,他們是寬依賴的邊界,所以分配在兩個不同的stage裏。
stage 1: groupBy("")
stage 2: foreachPartition
3 Partition
RDD劃分成邏輯分區,每個分區中包含rdd的部分數據。初始化的時候,分區數目由getPartitions確定,數據有compute確定。運算過程中由Partitioner確定分區數目和數據。
詳情參看:https://blog.csdn.net/adorechen/article/details/106202860
4 Task
Task是spark的任務運行單元,通常一個partition對應一個task。有失敗時另行恢復。
5 Executor
這裏的executor不是指的yarn中的node,而是指spark 總真正執行task任務的core。
曾經碰到的一個問題:
task上基本對應一個partition,每個partition有自己的index id,某特定index的partition是否可以在固定的executor上執行?
1). HashPartitioner --> Partition
由相同的HasnPartitioner shuffle 數據都會到相同partition index (partitionId)的partition中。
2)由Partition生成的Task 到 Executor路由分配
partition 生成的task 是否會路由到相同的executor上執行,取決於partition中數據是否位於該executor上。一般是不會固定該executor上。所以表現結果就是task(partition) --> executor 隨機分配。
舉個栗子:
第一次7個partition對應的任務 0-6, 分配到executorId: 2,6,1,4,3,7,5來執行。
第二次同樣的7個index的partition分配到executorId:6,7,1,4,2,3,5;
可以看出相同index的parition(task)並沒有被分配到相同的executor上來執行。