Apache spark Spark RDD:根据文本文件格式进行分区

Apache spark Spark RDD:根据文本文件格式进行分区,apache-spark,hadoop,rdd,hadoop-partitioning,Apache Spark,Hadoop,Rdd,Hadoop Partitioning,我有一个文本文件,其中包含数十GB的数据,我需要从HDFS加载这些数据,并将其作为RDD并行化。此文本文件使用以下格式描述项目。请注意,字母字符串不存在(每行的含义是隐式的),每行可以包含空格来分隔不同的值: 0001 (id) 1000 1000 2000 (dimensions) 0100 (weight) 0030 (amount) 0002 (id) 1110 1000 5000 (dimensions) 0220 (wei

我有一个文本文件,其中包含数十GB的数据,我需要从HDFS加载这些数据,并将其作为RDD并行化。此文本文件使用以下格式描述项目。请注意,字母字符串不存在(每行的含义是隐式的),每行可以包含空格来分隔不同的值:

0001  (id)
1000 1000 2000 (dimensions)
0100           (weight)
0030           (amount)
0002  (id)
1110 1000 5000 (dimensions)
0220           (weight)
3030           (amount)
我认为并行化此文件的最中间方法是从本地文件系统将其上载到HDFS,然后通过执行
sc.textFile(filepath)
来创建RDD。但是,在这种情况下,分区将取决于与文件对应的HDFS拆分

上述方法的问题在于每个分区可能包含不完整的项。例如:

分区1

0001           (id)
1000 1000 2000 (dimensions)
0100           (weight)
0030           (amount)
0002           (id)
1110 1000 5000 (dimensions)
分区2

0220           (weight)
3030           (amount)
因此,当我们为每个分区调用一个方法并将其相应的数据块传递给它时,它将收到标识为0002的项的不完整规范。这将导致在被调用方法内执行的计算的错误输出


为避免这个问题,对这个RDD进行分区或重新分区的最有效方法是什么?可以将每个分区的行数指定为4的倍数吗?如果是这样的话,应该由Hadoop或Spark来完成吗?

为什么不在将文件放入HDFS之前将行分组以避免此问题

xargs -L4 echo < file
hdfs dfs -put file /your/path
如果这样做,您可以使用Spark DataFrames API读取数据,这是一种更为优化的方法
加载文本文件以获得
RDD[String]
然后用于转换为
RDD[(String,Long)]
,其中tuple中的第二个属性是RDD中元素的索引号

用它的元素索引来压缩这个RDD。排序首先基于分区索引,然后是每个分区内项目的排序。因此,第一个分区中的第一项得到索引0,最后一个分区中的最后一项得到最大的索引

  • 使用索引作为行号(从0开始),我们可以对属于记录的行进行分组。例如,
    [0,1,2,3,4,5,6,7,8,9,…
  • 因为我们知道每个记录跨越(精确地)4行,索引的整数除以4(我们称之为idx_div)。这将导致前四行的0为
    idx_div
    ,接下来的四行将得到1为
    idx_div
    ,依此类推。例如
    [0,0,0,0,1,1,1,2,
    。这可以用于对所有(四行)进行分组属于一条记录的行,用于进一步分析和处理


不幸的是,我忘了解释每一原始行可以包含空格来分隔不同的值。我刚刚编辑了这个问题以包含这一方面。将您显示的行分组将与此冲突,因为空格不能代替换行符作为每一原始行的分隔符。很好,answer、 尽管如果您能对代码及其含义进行更详细的解释,这将非常有用。谢谢
0001  1000  0100  0030 
0002  1110  0220  3030
case class Record(id:String, dimensions:String, weight:String, amount:String)
val lines = sc.textFile("...")
val records = lines
    .zipWithIndex
    .groupBy(line_with_idx => (line_with_idx._2 / 4))  // groupBy idx_div
    .map(grouped_record => {
        val (idx_div:Long, lines_with_idx:Iterable[(String, Long)]) = grouped_record
        val lines_with_idx_list = lines_with_idx.toList.sortBy(_._2)  // Additional check to ensure ordering
        val lines_list = lines_with_idx_list.map(_._1)
        val List(id:String, dimensions:String, weight:String, amount:String) = lines_list
        new Record(id, dimensions, weight, amount)
    })