在数仓建立的最后一步,可能需要把数据从hive写入MySQL,我在学习中尝试写了一段这样的代码
val dbName:String = "ebs_ads_report"
spark.sql(s"show tables from $dbName")
.foreach(row=>{
val tableName: String = row.getString(1)
spark.table(s"$dbName.$tableName")
.repartition(1)
.write
.jdbc(getter.getUrl,tableName,getter.getConf)
})
思路是很简单的,就是在hive中show tables,遍历然后把每个表名存下来,用jdbc一个表一个表写入MySQL,结果遇到报错:Exception in thread "main" org.apache.spark.SparkException: Task not serializable at org.apache.spark.util.ClosureCleaner$.ensureSerializable(ClosureCleaner.scala:416) at org.apache.spark.util.ClosureCleaner$.clean(ClosureCleaner.scala:406) at org.apache.spark.util.ClosureCleaner$.clean(ClosureCleaner.scala:162) at org.apache.spark.SparkContext.clean(SparkContext.scala:2459)......
原因是spark任务中包含了不可序列化的对象或者闭包,应该是因为SparkSession不能序列化分析
解决方法如下:
spark.sql(s"show tables from $dbName")
.collect()
.foreach(row=>{
val tableName:String = row.getString(1)
spark.table(s"$dbName.$tableName")
.repartition(1)
.write
.jdbc(getter.getUrl, tableName, getter.getConf)
})
似乎只是加了一个collect算子,但是其实本质已经发生了变化。
这个操作把一个spark对象变成一个普通的Array[]对象,使用的foreach也就不是spark的foreach,而是scala的foreach(同样是foreach,竟然还是有区别的),这样就规避了这个序列化的问题,可以正常进行写入表数据了。