我们目前在使用scala语言编写的sparksql中面临性能问题。申请流程如下所述。
使用以编程方式指定架构在文件顶部创建数据框。此数据帧将保留在内存中的输入文件的精确复制。数据框中将有大约18列
var eqpDF = sqlContext.createDataFrame(eqpRowRdd, eqpSchema)
从步骤2中构建的第一个数据框创建过滤后的数据框。此数据框将在不同关键字的帮助下包含唯一的帐号。
var distAccNrsDF = eqpDF.select("accountnumber").distinct().collect()
使用步骤2和2中构建的两个数据帧。 3,我们将获取属于一个帐号的所有记录,并在过滤后的数据之上执行一些Json解析逻辑。
var filtrEqpDF =
eqpDF.where("accountnumber='" + data.getString(0) + "'").collect()
最后,json解析数据将被放入Hbase表
在这里,我们在数据框之上调用collect方法时遇到了性能问题。因为collect会将所有数据提取到单个节点然后进行处理,从而失去并行处理的好处。 同样在实际情况中,我们可以预期有100亿条数据记录。因此,由于内存或磁盘空间的限制,将所有这些记录收集到驱动程序节点中可能会使程序本身崩溃。
我不认为在我们的情况下可以使用take方法,它将一次获取有限数量的记录。我们必须从整个数据中获取所有唯一的帐号,因此我不确定是否采用方法 一次有限的记录,将符合我们的要求
感谢任何帮助以避免调用收集方法并遵循其他一些最佳做法。如果有人遇到类似的问题,代码片段/建议/ git链接将非常有用
val eqpSchemaString = "acoountnumber ....."
val eqpSchema = StructType(eqpSchemaString.split(" ").map(fieldName =>
StructField(fieldName, StringType, true)));
val eqpRdd = sc.textFile(inputPath)
val eqpRowRdd = eqpRdd.map(_.split(",")).map(eqpRow => Row(eqpRow(0).trim, eqpRow(1).trim, ....)
var eqpDF = sqlContext.createDataFrame(eqpRowRdd, eqpSchema);
var distAccNrsDF = eqpDF.select("accountnumber").distinct().collect()
distAccNrsDF.foreach { data =>
var filtrEqpDF = eqpDF.where("accountnumber='" + data.getString(0) + "'").collect()
var result = new JSONObject()
result.put("jsonSchemaVersion", "1.0")
val firstRowAcc = filtrEqpDF(0)
//Json parsing logic
{
.....
.....
}
}
答案 0 :(得分:2)
这种情况通常采用的方法是:
collect
,调用foreachPartition
:foreachPartition
将函数分别应用于基础Iterator[Row]
的每个分区(由DataFrame
表示)(分区是Spark的并行性的原子单位)这意味着每个执行程序都会打开一个连接(它不是可序列化的,但它位于函数的边界内,因此不需要通过网络发送)并独立地将其内容发送到HBase,而无需收集所有数据在驱动程序(或任何一个节点,就此而言)。
看起来你正在阅读一个CSV文件,所以类似下面这样的东西可以解决这个问题:
spark.read.csv(inputPath). // Using DataFrameReader but your way works too
foreachPartition { rows =>
val conn = ??? // Create HBase connection
for (row <- rows) { // Loop over the iterator
val data = parseJson(row) // Your parsing logic
??? // Use 'conn' to save 'data'
}
}
答案 1 :(得分:2)
如果您拥有大量数据,则可以在代码中忽略collect。
收集在驱动程序中将数据集的所有元素作为数组返回。在过滤器或其他返回足够小的数据子集的操作之后,这通常很有用。
这也会导致驱动程序内存不足,因为collect()会将整个RDD / DF提取到一台机器上。
我刚刚编辑了你的代码,这应该适合你。
var distAccNrsDF = eqpDF.select("accountnumber").distinct()
distAccNrsDF.foreach { data =>
var filtrEqpDF = eqpDF.where("accountnumber='" + data.getString(0) + "'")
var result = new JSONObject()
result.put("jsonSchemaVersion", "1.0")
val firstRowAcc = filtrEqpDF(0)
//Json parsing logic
{
.....
.....
}
}