Apache spark Spark RDD:根据文本文件格式进行分区
我有一个文本文件,其中包含数十GB的数据,我需要从HDFS加载这些数据,并将其作为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
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为
,接下来的四行将得到1为idx_div
,依此类推。例如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)
})