Spark Streaming中并行运行任务

在运行Spark Streaming程序时,有时我们需要并行化任务的执行。比如任务A需要每隔5s输出计算结果,任务B用到了时间窗口,每隔1hour计算一次并输出结果。如果全部串行运行的话,在运行任务B时,任务A就会被阻塞。可能B的执行需要3分钟,那这三分钟内A的计算结果就不能被实时看到了。在Spark程序内部(即每个Application中),任务是可以并行运行的。这个官网上有比较详细的介绍。但是Spark Streaming中,并行运行任务和上面官网中介绍的有些区别。

Scheduler Mode

首先,运行模式依然要设置为fair,这是必须的。但是Spark Streaming还有一个专用的参数用来设置可以并行的任务数:spark.streaming.concurrentJobs。这个参数的意思是可以同时运行多少任务,默认是1。如果我们需要同时运行1个以上的任务,就要把这个值调高。所以,如果我想并行运行A和B两个任务,就要这样配置:

    val conf: SparkConf = new SparkConf().setAppName("parallel")
    conf.set("spark.scheduler.mode","FAIR")
    conf.set("spark.streaming.concurrentJobs","2")

把spark.streaming.concurrentJobs设置为2,就可以同时运行两个任务了。

Fair Scheduler Pools

虽然调整了可并行运行任务的数量,可是默认情况下,所有的任务都是以同样的权重运行的。如果我们去看Spark的WEB UI界面,会在Stage标签内看到default pool,所有的任务都运行在默认的pool中。可是如何调整不同类型任务占用资源的权重呢?这个在上面官网链接中也有比较详细的介绍。可是在官网的介绍中,要求在不同的线程提交任务才可以,那么Spark Streaming如何在不同线程提交任务呢?
答案也很简单,Spark Streaming不同的Action之间本来就是多线程执行的。所以可以使用这种方式设置:

dstream.foreachRDD(rdd =>
   rdd.sparkContext.setLocalProperty("spark.scheduler.pool","pool_a")
)

这样就可以使用不同的pool了。这时候在WEB UI界面的Stage标签中就可以看到不同的pool了。而pool的资源分配权重和pool内部的调用方式依然在 $SPARK_HOME/conf/fairscheduler.xml 中配置,详情请参考上面的官网链接。

### 配置和优化 Spark Streaming 并行度 在 Spark Streaming 中,并行度主要由批处理间隔 (Batch Interval) 和块间隔 (Block Interval) 决定。默认情况下,`blockInterval` 设置为 200 毫秒,而 `batchInterval` 取决于应用程序的具体配置[^5]。 #### 批处理间隔与并行度的关系 当 `batchInterval` 设为 5 秒时,默认分区数量等于 `batchInterval / blockInterval` 即 25 个分区。然而,实际数据量可能不足以填充这些分区,导致某些批次仅有少量甚至单个分区被创建。这种现象会影响作业的整体性能以及资源利用率。 为了更好地控制并行度,可以考虑以下几个方面: - **调整 Block Interval**: 减小 `spark.streaming.blockInterval` 参数值可增加每批次内的 Partition 数目;反之则减少。不过需要注意的是,过短的时间可能导致过多的小任务生成,反而降低效率。 - **设定最小输入分片数**: 使用 `minPartitions` 参数指定读取源时所需的最少分区数目,确保即使数据稀疏也能维持一定水平的并发执行能力。 - **自定义接收器逻辑**: 如果应用依赖于 Receiver 接收外部流式数据,则可以通过实现自定义 Receiver 来更灵活地管理缓冲区大小及写前日志功能(通过开启 `spark.streaming.receiver.writeAheadLog.enable=true`),从而间接影响最终形成的 RDD 分区结构[^2]。 针对提到的数据写入 Iceberg 表过程中产生的大量小文件问题,除了上述措施外还可以采取以下策略来缓解 Hive Metastore 压力: - **合并小文件**: 定期触发 Compaction 操作以合并多个小文件成较大规模的单一文件; - **调节 Shuffle 并行度**: 对于涉及 shuffle 的转换操作,适当增大 `spark.sql.files.maxPartitionBytes` 或者显式调用 repartition() 方法重新划分数据集,避免因过高并行度造成过多输出片段[^4]。 ```scala // 示例代码:显示设置 partition 数量 val df = spark.read.format("iceberg").load("table_path") df.repartition(num_partitions).write.mode("append").format("iceberg").save("output_table_path") ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值