Java 如何加载大型csv文件、验证每一行并处理数据

Java 如何加载大型csv文件、验证每一行并处理数据,java,csv,apache-spark,hadoop,data-ingestion,Java,Csv,Apache Spark,Hadoop,Data Ingestion,我希望验证超过6亿行、最多30列的csv文件的每一行(解决方案必须处理该范围内的多个大型csv文件) 列可以是文本、日期或金额。csv必须使用40条规则进行验证,有些规则将检查金额的正确性,有些规则将检查日期(格式),等等 必须保存每个验证规则的结果,并在之后显示 数据验证后,将应用第二阶段的验证规则,这一阶段基于总和、平均值……每个规则的结果也必须保存 我正在使用Spark加载文件。与 session.read().format("com.databricks.spark.csv").opti

我希望验证超过6亿行、最多30列的csv文件的每一行(解决方案必须处理该范围内的多个大型csv文件)

列可以是文本、日期或金额。csv必须使用40条规则进行验证,有些规则将检查金额的正确性,有些规则将检查日期(格式),等等

必须保存每个验证规则的结果,并在之后显示

数据验证后,将应用第二阶段的验证规则,这一阶段基于总和、平均值……每个规则的结果也必须保存

我正在使用Spark加载文件。与

session.read().format("com.databricks.spark.csv").option("delimiter",
         "|").option("header", "false").csv(csvPath)

要在每行上迭代,我看到有两个选项:

  • 使用
    dataset.map(行->{something})
    “Something”应该验证每一行并将结果保存在某个地方
但是,由于“something”块将在executors中执行,我不知道如何将其返回到驱动程序或将其存储在某个可以从驱动程序进程中检索的位置

  • 第二个选项是使用dataset.collect(收集):但它会导致outofmemory,因为所有数据都将加载到驱动程序中。我们可以使用“take”方法,然后从数据集中删除子集(使用过滤器)并重复该操作,但我不习惯使用这种方法
我想知道是否有人能给我推荐一种稳健的方法来处理这类问题。基本上,在验证规则的第二阶段保留Spark,并使用Spark或其他framwrok接收文件,执行并生成第一组验证规则


提前感谢您的帮助

您可以使用
SparkSession
读取CSV文件,然后按列对数据进行分区并批量处理数据。例如,将数据写入不需要太多处理的外部数据库

dataFrame
    .write
    .mode(saveMode)
    .option("batchsize", 100)
    .jdbc(url, "tablename", new java.util.Properties())
如果您的业务逻辑要求您处理数据集/数据帧的每一行,那么您可以使用
df.map()
。如果您的逻辑可以同时在多个RDD上工作,则可以使用
df.mapPartition()
。使用
mapPartition
转换比使用
map
转换具有高每记录开销的任务执行得更好


考虑初始化数据库的情况。如果使用
map()
foreach()
,则需要初始化的次数将等于RDD中的元素数。然而,如果我们使用
mapPartitions()
,我们需要初始化的次数将等于分区的数量

您可以简单地将带有检查结果的列附加到原始数据帧中,并使用一组规则UDF来执行实际的验证,如下所示:

    object Rules {
      val rule1UDF = udf(
        (col1: String, col2: String) => {
         // your validation code goes here
         true // the result of validation
      }
    }
    // ...
    val nonAggregatedChecksDf = df
       .withColumn("rule1_result", Rules.rule1UDF("col1", "col2"))
       .withColumn("rule2_result", Rules.rule2UDF("col1", "col3"))
       .select("id", "rule1_result", "rule2_result", <all the columns relevant for the aggregation checks>)

    val aggregatedChecksDf = nonAggregatedChecksDf
       .agg(<...>)
       .withColumn("rule3_result", Rules.rule3UDF("sum1", "avg2"))
       .withColumn("rule4_result", Rules.rule4UDF("count1", "count3"))
       .select("id", "rule1_result", "rule2_result", "rule3_result", "rule4_result")
这将更快,因为写入是由所有执行器并行完成的,并且驱动程序不会成为瓶颈。这也很可能有助于避免OOM问题,因为内存使用量分布在所有执行器上

    object Rules {
      val rule1UDF = udf(
        (col1: String, col2: String) => {
         // your validation code goes here
         true // the result of validation
      }
    }
    // ...
    val nonAggregatedChecksDf = df
       .withColumn("rule1_result", Rules.rule1UDF("col1", "col2"))
       .withColumn("rule2_result", Rules.rule2UDF("col1", "col3"))
       .select("id", "rule1_result", "rule2_result", <all the columns relevant for the aggregation checks>)

    val aggregatedChecksDf = nonAggregatedChecksDf
       .agg(<...>)
       .withColumn("rule3_result", Rules.rule3UDF("sum1", "avg2"))
       .withColumn("rule4_result", Rules.rule4UDF("count1", "count3"))
       .select("id", "rule1_result", "rule2_result", "rule3_result", "rule4_result")
aggregatedChecksDf
    .select("id", "rule1_result", "rule2_result", "rule3_result", "rule4_result")
    .write
    .mode(saveMode)
    .parquet(path)