我正在尝试使用Spark从Greenplum数据库中提取增量数据。我们为每个表提供了递增数据,并带有一个名为transactionId
的键。
每个transactionId
可以包含一行或许多行的数据。所有这些都存储在元数据表incKeyTable
中。
我们还具有另一个元数据表transactionID
中每个表的最后移动的incKeyLoads
。该表每个表包含一个条目,它是生产表中最后更新的transactionId
。
为了找出每个表的增量transactionid
,我想出了以下逻辑。
val spark = SparkSession.builder().master("yarn").enableHiveSupport().config("hive.exec.dynamic.partition", "true").config("hive.exec.dynamic.partition.mode", "nonstrict").getOrCreate()
import spark.implicits._
Class.forName("org.postgresql.Driver").newInstance()
val tableStatus = s"select tablename, last_update_transaction_id from prod.incKeyLoads where source_system='DB2' and tablename='table1' and final_stage='PROD' and load='Successfull'"
val tableMetaDF = spark.read.format("jdbc").option("url", "url").option("dbtable", s"(${tableStatus}) as LoadedData").option("user", "user").option("password", "pwd").load()
val lutransIdTableMap = tableMetaDF.map(r => (r.getString(0),r.getLong(1))).collect().toMap
现在,我在斯卡拉地图中具有上一次更新的交易ID,如下所示:
lutransIdTableMap.foreach(println) =
(table1 -> 123)
(table2 -> 113)
(table3 -> 122)
...
(tableN -> 098)
要找出最新的transactionId
(增量数据),请编写以下逻辑查询元数据表:incKeyTable
Class.forName("com.pivotal.jdbc.GreenplumDriver").newInstance()
def sortLogIds(incTransIds:DataFrame, lastMovedTransId:Long, tablename: String):String = {
val returnMsg = "Full loads on this table"
val count = incTransIds.where($"load_type" === "FULLLOAD").count
if(count == 0) {
incTransIds.createOrReplaceTempView("incTransID")
val execQuery = s"SELECT transactionId from incTransID order by transactionId desc"
val incLogIdDf = spark.sql(execQuery)
incLogIdDf.show
val pushTransIds = "select * from schema.tablename where transactionID in(" + "'" + incLogIdDf.select($"transactionId").collect().map(_.getInt(0).toString).mkString("','") + "')"
pushLogIds
} else {
println("Full load count is greater than zero..")
returnMsg
}
}
var incTransIdMap = Map[String, String]()
lutransIdTableMap.keys.foreach(keyTable => if(lutransIdTableMap(keyTable) !=0) {
val tablename = keyTable.split("\\.") // Tablename = schema.tablename
val cdf = spark.read.format("jdbc").option("url", "url").option("dbtable", s"(select transactionId, load_type, source_system, tablename from schema.incKeyTable where source_system='DB2' and target_table='${tablename(1)}' and transactionId > ${lutransIdTableMap(keyTable)}) as controlTableDF").option("user", "user").option("password", "pwd").load()
incTransIdMap += (keyTable -> sortLogIds(cdf, lutransIdTableMap(keyTable), tablename(1)))
}
)
此方法有效,但是花了很长时间,我才能在表搜索之前从greenplum提取全部数据,因为数据帧cdf是巨大的数据。我尝试缓存数据帧:cdf,但是其中包含将近500万行,建议不要缓存这么大的表以进行缓存。 我想不出其他方法可以更快地进行搜索。任何人都可以让我知道使这一过程高效的想法吗?
答案 0 :(得分:1)
问题中的代码不能是您实际运行的代码,因为您将返回pushLogIds
中的sortLogIds
(从未定义),并且选择的是schema.tablename
而不是from s"schema.$tablename"
。这使得很难确定到底发生了什么...
也就是说,从大数据处理模式的角度来看,您的方法存在一些潜在的问题:
迭代而不是UNION转换。在其他条件相同的情况下,最好不要发布多个单独的查询,然后在驱动程序上组合结果,而是考虑发布单个驱动器的方法。查询。这就是优化器有机会提供帮助的方式。在您的情况下,请考虑创建一个Greenplum视图,该视图结合了lutransIdTableMap
中的所有表。
操作而不是联接转换。。在sortLogIds
中,您正在执行count
操作,只是为了决定是否运行其他查询。在其他条件相同的情况下,最好通过join转换来表达这一点,以延迟操作的执行。稍后,您发出一个show
,在幕后它等效于take(n)
。这个动作真的必要吗?稍后,您使用collect
来生成要在IN
运算符中使用的SQL表达式。这是另一个示例,您应改为使用联接。总而言之,您将执行由incTransId
表示的相同Greenplum基本查询三次。如果您坚持这种处理方式,则应该以某种方式绝对坚持incTransId
。
SQL汇编而不是DSL使用。通常,如果您是通过编程语言而不是通过SparkSQL使用Spark,则应该使用DSL而不是像这样组装SQL表达式。字符串。这样,您无需重新定义视图等。
在这里,没有完整的代码,也没有确切的Greenplum模式+分发策略+索引(如果有)以及所涉及的数据大小,有太多需要修复的地方。但是,以上内容应为您提供一个起点。
这里是如何从使用迭代切换到并集的示例。
val allData = Map("table1" -> 101, "table2" -> 212)
.map { case (tableName, id) =>
spark.table(tableName).withColumn("id", lit(id))
}
.reduceLeft(_ union _)
以下是如何使用联接而不是collect
+ IN
的示例。
val allIds = spark.range(100)
val myIds = spark.createDataset(Seq(11, 33, 55, 77, 99)).toDF("id")
allIds.where('id.isin(myIds.as[Int].collect: _*)) // premature action
allIds.join(myIds, Seq("id")) // inner join delays action
上面的示例还显示了如何将数据集与collect
一起使用,例如,将.collect().map(_.getInt(0).toString)
替换为.as[String].collect
,这更加简单,安全和快捷。
希望这会有所帮助!