Spark 1.5.2 DataFramesUDF在对象重用时避免竞争条件

时间:2017-01-13 12:03:14

标签: scala apache-spark dataframe spark-dataframe race-condition

这里的问题是你如何重用UDF的对象但避免竞争条件?

我在我的spark应用程序中使用UDF,并且由于竞争条件,单元测试似乎不确定。有时他们有时候会失败......

我试图通过创建对象并将它们传递给UDF来强制重用对象以提高效率。但是,似乎单独的“测试”共享相同的spark上下文和JVM正在使用这些对象并导致错误。

def reformatDate(input:String,sdfIn:SimpleDateFormat,sdfOut:SimpleDateFormat): String ={
    sdfOut.format(sdfIn.parse(input))
  }

  val datePartitionFormat = new SimpleDateFormat("yyyyMMdd")
  val dTStampFormat = new SimpleDateFormat("yyyy/MM/dd")
  val validDateFormat = new SimpleDateFormat("yyyy-MM-dd")

  val partitionToDateUDF = udf(reformatDate(_:String,datePartitionFormat,validDateFormat))
  val dTStampToDateUDF= udf(reformatDate(_:String,dTStampFormat,validDateFormat))

有时当我运行单元测试时,我会使用此函数出现以下错误:

  

17/01/13 11:45:45 ERROR Executor:阶段2.0中任务0.0的异常   (TID 2)java.lang.NumberFormatException:多个点   sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890)     在sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)at at   java.lang.Double.parseDouble(Double.java:538)at   java.text.DigitList.getDouble(DigitList.java:169)at   java.text.DecimalFormat.parse(DecimalFormat.java:2056)at   java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1867)at   java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)at   java.text.DateFormat.parse(DateFormat.java:364)at   com.baesystems.ai.engineering.threatanalytics.microbatch.processor.transformers.metric.mDnsPreviouslySeenDomainsStartOfDayDF $ .reformatDate(mDnsPreviouslySeenDomainsStartOfDayDF.scala:22)

我使用这样的功能:

val df = df2
  .filter(
    datediff(
      to_date(partitionToDateUDF($"dt"))
      ,to_date(dTStampToDate($"d_last_seen"))
    ) < 90
  )

并且在调试时发现输入“df2”为:

+-----------+--------+-------------------------+--------------------------------+
|d_last_seen|      dt|partitionToDateUDF($"dt")|dTStampToDateUDF($"d_last_seen")|
+-----------+--------+-------------------------+--------------------------------+
| 2016/11/02|20161102|2016-11-02               |2016-11-02                      |
| 2016/11/01|20161102|2016-11-02               |2016-11-01                      |
+-----------+--------+-------------------------+--------------------------------+

我使用conf.setMaster(“local [2]”),可能是spark使用线程并因此在本地运行时共享相同的JVM,但是当部署时不会发生这种情况,因为单独的执行程序将拥有自己的JVM和它们自己的对象实例化?

1 个答案:

答案 0 :(得分:2)

SimpleDateFormat不是线程安全的(例如参见Why is Java's SimpleDateFormat not thread-safe?)。这意味着如果你在任何UDF中使用它(即使在一个单独的Spark作业中),你可能会得到意想不到的结果,因为spark会在几个任务中使用你的UDF,这些任务在单独的线程上运行以多个线程同时访问它结束。对于本地模式和实际分布式集群都是如此 - 每个执行程序上的多个线程将使用单个副本。

要克服这一点 - 只需使用 线程安全的不同格式化程序,例如Joda的DateTimeFormatter