Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/xml/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
优化Spark Scala作业-许多任务,嵌套映射需要数小时,XML解析_Xml_Scala_Apache Spark_Hdfs - Fatal编程技术网

优化Spark Scala作业-许多任务,嵌套映射需要数小时,XML解析

优化Spark Scala作业-许多任务,嵌套映射需要数小时,XML解析,xml,scala,apache-spark,hdfs,Xml,Scala,Apache Spark,Hdfs,我正在spark shell中运行一个spark作业,它已经执行了将近80多个小时,必须有某种方法来扩展它。以下是我在启动作业时提交的配置以及正在运行的代码 spark-shell --master \ yarn \ --num-executors 100 \ --name cde_test \ --executor-cores 4 \ --executor-memory 5g \ --driver-cores 2 \ --driver-memory 3g \ --jars ./spark_ja

我正在spark shell中运行一个spark作业,它已经执行了将近80多个小时,必须有某种方法来扩展它。以下是我在启动作业时提交的配置以及正在运行的代码

spark-shell --master \
yarn \
--num-executors 100 \
--name cde_test \
--executor-cores 4 \
--executor-memory 5g \
--driver-cores 2 \
--driver-memory 3g \
--jars ./spark_jars/spark-xml_2.11-0.8.0.jar \
--verbose
以下是资源管理器UI工具上执行者信息的图片:

我想用spark XML解析XML文件,提取某些字段并保存到CSV。我认为增加执行器的数量会加快工作的速度,因为这些都是小而快速的低内存任务,但不确定我是否做对了,或者代码的编写方式是否会阻止并行执行。下面的代码,并感谢任何和所有的帮助

import org.apache.hadoop.fs._
import collection.mutable._
import spark.implicits._
import java.io.File
import java.util.regex.Pattern
import org.apache.spark.sql._
import org.apache.spark.SparkContext
import org.apache.spark.rdd.RDD
import scala.util.control.Exception._
import org.apache.commons.io.FilenameUtils  
import org.apache.commons.lang.StringEscapeUtils
import org.apache.hadoop.conf.Configuration

def merge(srcPath: String, dstPath: String): Unit =  {
   val hadoopConfig = new Configuration()
   val hdfs = FileSystem.get(hadoopConfig)
   FileUtil.copyMerge(hdfs, new Path(srcPath), hdfs, new Path(dstPath), true, hadoopConfig, null) 
   // the "true" setting deletes the source files once they are merged into the new output
}

object HdfsUtils {
  def pathExists(path: String, sc: SparkContext): Boolean = {
    val conf = sc.hadoopConfiguration
    val fs = FileSystem.get(conf)
    fs.exists(new Path(path))
  }

  def getFullPath(path:String, sc: SparkContext): String = {
    val conf = sc.hadoopConfiguration
    val fs = FileSystem.get(conf)
    fs.getFileStatus(new Path(path)).getPath().toString
  }

  def getAllFiles(path:String, sc: SparkContext): Seq[String] = {
    val conf = sc.hadoopConfiguration
    val fs = FileSystem.get(conf)
    val files = fs.listStatus(new Path(path))
    files.map(_.getPath().toString)
  }
}

//Four different mapping functions
val path_list = Seq("path_1_for_first_directory",
"path_2_for_second_directory")

path_list.foreach ( path => {
val hdfs_directory = HdfsUtils.getAllFiles(path, sc)

hdfs_directory.foreach( intermediate_folder => {
val intermediate_folders = HdfsUtils.getAllFiles(intermediate_folder, sc)

intermediate_folders.foreach( final_folder => {
val hdfs_files = HdfsUtils.getAllFiles(final_folder, sc)

hdfs_files.foreach( xml_file => {

val date = raw"(\d{4})-(\d{2})-(\d{2})".r
val directory_date = date.findFirstIn(xml_file).
getOrElse(xml_file)

//Ignore meta files
if (xml_file.contains("META") || xml_file.contains("meta")){


} else if (xml_file.contains(".xml") || xml_file.contains(".XML")){


try{

val xml_df = spark.
read.
format("xml").
option("rowTag","root").
option("treatEmptyValuesAsNulls","true").
option("nullValue", null).
option("emptyValue", null).
load(xml_file)

val info_df = xml_df.
select(
  substring($"column_1",0,8).alias("date"),
  substring($"column_2",9,20).alias("time"),
  $"column_3".alias("first_name").cast("string"),
  $"column_4".alias("last_name").cast("string"),
  $"column_5".alias("birthday").cast("string"),
  $"column_6".alias("street").cast("string"),
  $"column_7".alias("city").cast("string"),
  $"column_8".alias("state").cast("string"),
  $"column_9".alias("zip_code").cast("string"),
  $"column_10".alias("country").cast("string")
)

val outputfile = "/path_to_output/"
var filename = s"$directory_date"
var outputFileName = outputfile + filename 


info_df.write
    .format("csv")
    .option("header", "false")
    .option("sep","|")
    .mode("append")
    .save(outputFileName)

    } 

    catch{ 
      case _: RuntimeException => {}
      case _: Exception => {}
    }
}
})
})
})
})

您在Seq上使用的是
foreach
,这是顺序的(正如Hristo Iliev所提到的)。如果您有很多文件,但大部分文件都比较小,那么一次处理一个文件可能就是速度慢的原因

  • 您可以使用通配符,而不是遍历HDFS文件。您可以一次将多个文件读入更大的数据帧;例如,在这里,我们一次处理一个月:
spark.read.format(“xml”).load(“/somepath/*/YYYY-MM-*.xml”)
注意
/*/
表示“中间目录”。对您更好的方法可能取决于这些中间目录是否有更具体的模式,或者它们是否也取决于日期

  • 如果仍希望按日期组织多个输出文件,请将
    partitionBy
    info_df
    一起使用,以便根据键将较大的数据框输出到多个日期目录/文件中。下面是一个例子:

据我所知,您并不是在用不同的Spark执行器并行读取不同的XML文件。相反,您正在按顺序读取驱动程序中的所有文件,为每个文件创建一个新的数据帧,这是一个非常昂贵的过程,尤其是在您从HDFS读取文件的情况下。您需要为所有文件名创建一个数据框,对其进行过滤,然后使用
foreach()
。使用其他方法在每个worker中本地加载XML内容,而不是
spark XML
,因为后者会创建数据帧。好的,您对spark XML的替代品有什么建议吗?请注意,我的集群没有启用PySpark,因此Python库不是一个选项。如果您认为spark xml不是Scala一流的选择,那么您将如何着手执行您列出的内容?不确定如何实现这一点。它是广泛的,但不是性能最好的,这就是为什么Spark XML在JavaStaxAPI之上有自己的XML解析器。如果您的文件很小,我建议使用Scala XML API。否则,请查看
javax.xml
命名空间中的可用内容。或者考虑分离相关的XML文件,这样您就可以使用Spark XML大量读取它们,如ELinda的回答所示。
foreach
本身没有问题。当对正确的集合类型(如RDD或DataFrame)调用时,它会并行执行!我应该指定正在使用foreach for Seq。感谢您的帮助——这非常有用!