Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/apache-spark/6.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
Scala collect方法存在SparkSQL性能问题_Scala_Apache Spark_Spark Dataframe - Fatal编程技术网

Scala collect方法存在SparkSQL性能问题

Scala collect方法存在SparkSQL性能问题,scala,apache-spark,spark-dataframe,Scala,Apache Spark,Spark Dataframe,我们目前在用scala语言编写的sparksql中面临一个性能问题。应用程序流程如下所述 Spark应用程序从输入hdfs目录读取文本文件 使用编程方式指定架构在文件顶部创建数据框。此数据帧将是内存中保存的输入文件的精确复制。在数据帧中将有大约18列 var eqpDF=sqlContext.createDataFrame(eqpRowRdd,eqpSchema) 从步骤2中构造的第一个数据帧创建过滤后的数据帧。此数据框将在distinct关键字的帮助下包含唯一的帐号 var distAccNr

我们目前在用scala语言编写的sparksql中面临一个性能问题。应用程序流程如下所述

  • Spark应用程序从输入hdfs目录读取文本文件
  • 使用编程方式指定架构在文件顶部创建数据框。此数据帧将是内存中保存的输入文件的精确复制。在数据帧中将有大约18列

    var eqpDF=sqlContext.createDataFrame(eqpRowRdd,eqpSchema)

  • 从步骤2中构造的第一个数据帧创建过滤后的数据帧。此数据框将在distinct关键字的帮助下包含唯一的帐号

    var distAccNrsDF=eqpDF.select(“accountnumber”).distinct().collect()

  • 使用在步骤2和3中构造的两个数据帧,我们将获得属于一个帐号的所有记录,并在过滤后的数据上执行一些Json解析逻辑

    var filterqpdf=
    eqpDF.where(“accountnumber=”+data.getString(0)+“”).collect()

  • 最后,json解析的数据将被放入Hbase表中

  • 在这里,我们在调用数据帧顶部的collect方法时面临性能问题。因为collect会将所有数据提取到单个节点中,然后进行处理,因此失去了并行处理的优势。 此外,在真实场景中,我们可以预期将有100亿条数据记录。因此,由于内存或磁盘空间的限制,将所有这些记录收集到驱动程序节点可能会使程序本身崩溃

    我认为take方法不能用于我们的情况,因为它一次只能获取有限数量的记录。我们必须从整个数据中获得所有唯一的帐号,因此我不确定是否采用方法,这需要 每次记录有限,将符合我们的要求

    感谢您为避免调用collect方法提供的帮助,并提供一些其他最佳实践。如果有人遇到过类似的问题,代码片段/建议/git链接将非常有用

    代码片段
    在这种情况下通常采取的方法是:

    • 调用
      foreachPartition
      :而不是
      collect
      foreachPartition
      将函数分别应用于基础
      数据帧的每个分区(由
      迭代器[Row]
      表示)(分区是Spark并行的原子单位)
    • 该函数将打开到HBase的连接(从而使每个分区都有一个连接),并通过该连接发送所有包含的值
    这意味着每个执行器打开一个连接(该连接不可序列化,但位于函数的边界内,因此不需要通过网络发送),并独立地将其内容发送到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'
        }
      }
    
    spark.read.csv(输入路径)。//使用DataFrameReader,但您的方式也适用
    foreachPartition{rows=>
    val conn=???//创建HBase连接
    
    对于(row而言,在这种情况下通常采用的方法是:

    • 调用
      foreachPartition
      :而不是
      collect
      foreachPartition
      将函数分别应用于基础
      数据帧的每个分区(由
      迭代器[Row]
      表示)(分区是Spark并行的原子单位)
    • 该函数将打开到HBase的连接(从而使每个分区都有一个连接),并通过该连接发送所有包含的值
    这意味着每个执行器打开一个连接(该连接不可序列化,但位于函数的边界内,因此不需要通过网络发送),并独立地将其内容发送到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'
        }
      }
    
    spark.read.csv(inputPath)。//使用DataFrameReader,但您的方式也可以
    foreachPartition{rows=>
    val conn=???//创建HBase连接
    
    对于(row),如果有大量数据,可以忽略代码中的collect

    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 
                  {
                     .....
                     .....
                  }
                }
    

    如果有大量数据,则可以忽略代码中的collect

    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 
                  {
                     .....
                     .....
                  }
                }
    

    您实际上想要做什么?只需写入Hbase表?如果是这样,为什么要收集或获取?收集和获取仅用于查看样本数据。除此之外,不需要使用收集或获取。基本上,我们需要将属于同一帐号的所有数据分组(在源文件中)并且分组的数据必须推送到hbase。我们使用Collect是因为,为了全局查找不同的帐号。如果我们不调用Collect,我们如何全局查找可能分布在多个节点中的唯一帐号。您认为呢