1.问题:
spark中使用多线程并行执行多个独立的job,每个线程中分别在foreachPartition算子中使用累加器LongAccumulator。众所周知,累加器是在每个excutor中改变值然后在driver中汇总得到最终累加器的值。问题来了,返回到driver中累加器的结果,是每个线程执行结果累加的值,还是所有线程执行完后结果累加,也可以理解为多个累加器的结果在driver中是独立的,还是多个累加器结果累加?
2、结论:
diver中,多个累加器的结果是分别独立的,而不是把所有累加器的结果累加汇总,多线程中累加器是独立的。
3、测试:
下面使用项目中的代码片段说明:
3.1 多线程代码片段:每个线程调用distribution(tableName, p, prop, spark, brandinfoBro)方法
val countDownLatch = new CountDownLatch(passengerDatabases.length)
passengerDatabases.foreach(p => {
val runnable: Runnable = new Runnable {
override def run(): Unit = {
try {
distribution(tableName, p, prop, spark, brandinfoBro)
} catch {
case e: Exception => {
}
} finally {
countDownLatch.countDown();
}
}
}
pool.execute(runnable)
})
//等待全部表执行结束
countDownLatch.await(10, TimeUnit.MINUTES);
}
pool.shutdown()
spark.close()
3.2 distribution(tableName, p, prop, spark, brandinfoBro)方法中代码片段:
定义累加器,再调用distributionToMysql方法,在其中改变累加器的值,通过查看下面日志推测结果
val errorCount: LongAccumulator = spark.sparkContext.longAccumulator("errorCount")
println(s"${dateUtils.currentTime()} 表:${tableName} 分发到${p.id}_${p.branchId}_${p.source_name} 数据库,${errorCount}累加器初始值:${errorCount.count}")
distributionToMysql(p, finalDF, tableName, prop, errorCount)
println(s"${dateUtils.currentTime()} 表:${tableName} 分发到${p.id}_${p.branchId}_${p.source_name} 数据库,${errorCount}累加器最终值:${errorCount.count}")
3.3 distributionToMysql(p, finalDF, tableName, prop, errorCount)方法中代码片段:
foreachPartition中操作如果发生错误则累加器值加1,errorCount.add(1)。测试时手动修改代码使其报错。
updateDF.foreachPartition(split => {
try {
Class.forName(p.driver_class)
conn = ConnectUtils.getConnection2(p.user_name, p.user_pass, p.jdbc_url)
insertPstmt = conn.prepareStatement(insertSql)
updatePstmt = conn.prepareStatement(updateSql)
selectPstmt = conn.prepareStatement(selectSql)
split.foreach(x => {
updatePstmt = setPrepareStatement(updatePstmt, x, columnTypes)
updatePstmt.setObject(columnTypes.length + 1, x.getAs(uniqueKey), Types.INTEGER)
//更新行数
val updateNum: Int = updatePstmt.executeUpdate()
})
}
} catch {
case e: Exception => {
logger.error(s"${dateUtils.currentTime()} 表:${tableName} 分发到 ${p.id}_${p.branchId}_${p.source_name} 数据库失败,原因:", e)
errorCount.add(1)
}
} finally {
ConnectUtils.close(conn, updatePstmt)
ConnectUtils.close(conn, selectPstmt)
ConnectUtils.close(conn, insertPstmt)
}
})
3.4 通过查看累加器数值的日志得到结论:
2022-07-22 10:01:20 表:plazainfo 分发到157_420_杭州富阳 数据库,LongAccumulator(id: 406, name: Some(errorCount), value: 0)累加器初始值:0
2022-07-22 10:01:21 表:plazainfo 分发到157_420_杭州富阳 数据库,LongAccumulator(id: 406, name: Some(errorCount), value: 1)累加器最终值:1
2022-07-22 10:01:21 表:plazainfo 分发到13_181_北京丰科 数据库,LongAccumulator(id: 216, name: Some(errorCount), value: 1)累加器最终值:1
2022-07-22 10:01:21 表:plazainfo 分发到422_459_咸阳秦都 数据库,LongAccumulator(id: 436, name: Some(errorCount), value: 0)累加器初始值:0
2022-07-22 10:01:23 表:plazainfo 分发到422_459_咸阳秦都 数据库,LongAccumulator(id: 436, name: Some(errorCount), value: 1)累加器最终值:1
发现每个线程的累加器初始值是1,最终值是1,并没有将结果值全部汇总到driver端累加为3.