我有文件,其中每个元组跨越多行,例如:
START
name: Jim
phone: 2128789283
address: 56 2nd street, New York, USA
END
START
name: Tom
phone: 6308789283
address: 56 5th street, Chicago, 13611, USA
END
.
.
.
以上是我文件中的2个元组。我编写了我的UDF定义了一个getNext()
函数,它检查它是否为START然后我将初始化我的元组;如果是END那么我将返回元组(来自字符串缓冲区);否则我只会将字符串添加到字符串缓冲区。
适用于文件大小小于64 MB的HDFS块大小(在Amazon EMR上),而对于大于此大小的大小,它将失败。我试着谷歌,找到这个blog post。 Raja的解释很容易理解,他提供了一个示例代码。但是代码正在实现RecordReader
部分,而不是getNext()
来实现猪LoadFunc
。只是想知道是否有人有处理多线猪元组分裂问题的经验?我应该继续在猪中实施RecordReader
吗?如果是这样,怎么样?
感谢。
答案 0 :(得分:1)
您可以将输入预处理为提及的 Guy ,也可以应用here所述的其他技巧。
我认为最干净的解决方案是实现一个自定义InputFormat(及其RecordReader),它创建一个记录/ START-END。 Pig的LoadFunc位于Hadoop的InputFormat的顶部,因此您可以定义LoadFunc将使用哪个InputFormat。
自定义LoadFunc的原始骨架实现如下所示:
import java.io.IOException;
import org.apache.hadoop.mapreduce.InputFormat;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.RecordReader;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.pig.LoadFunc;
import org.apache.pig.backend.hadoop.executionengine.mapReduceLayer.PigSplit;
import org.apache.pig.data.Tuple;
import org.apache.pig.data.TupleFactory;
public class CustomLoader extends LoadFunc {
private RecordReader reader;
private TupleFactory tupleFactory;
public CustomLoader() {
tupleFactory = TupleFactory.getInstance();
}
@Override
public InputFormat getInputFormat() throws IOException {
return new MyInputFormat(); //custom InputFormat
}
@Override
public Tuple getNext() {
Tuple result = null;
try {
if (!reader.nextKeyValue()) {
return null;
}
//value can be a custom Writable containing your name/value
//field pairs for a given record
Object value = reader.getCurrentValue();
result = tupleFactory.newTuple();
// ...
//append fields to tuple
}
catch (Exception e) {
// ...
}
return result;
}
@Override
public void prepareToRead(RecordReader reader, PigSplit pigSplit)
throws IOException {
this.reader = reader;
}
@Override
public void setLocation(String location, Job job) throws IOException {
FileInputFormat.setInputPaths(job, location);
}
}
LoadFunc
初始化InputFormat
及其RecordReader
后,它会找到数据的输入位置,并开始从recordReader获取记录,创建生成的元组( getNext())直到输入完全被读取。
关于自定义InputFormat的一些评论:
我创建了一个自定义的InputFormat,其中RecordReader是其修改版本
org.apache.hadoop.mapreduce.lib.input.LineRecordReader
:大多数方法都会
保持不变,除了initialize()
:它会调用自定义LineReader
(基于org.apache.hadoop.util.LineReader
)。
InputFormat的键是行偏移量(Long),值将是自定义
可写。这将把记录的字段(即START-END之间的数据)保存为键值对列表。每次调用RecordReader的nextKeyValue()
时,记录都会被LineReader写入自定义Writable。整个事情的要点是你是怎样的
实施 LineReader.readLine()
。
另一种可能更简单的方法是更改TextInputFormat的分隔符(可在Hadoop 0.23中配置,参见textinputformat.record.delimiter
)
适用于您的数据结构的(如果可能的话)。在这种情况下,您最终会将数据放在Text
中,您需要从中分割并提取KV对并进入元组。
答案 1 :(得分:0)
如果可以作为分隔符启动,可能在代码下面没有UDF
SET textinputformat.record.delimiter 'START';
a = load '<input path>' as (data:chararray);
dump a;
输出看起来像:
(
name: Jim
enter code here`phone: 2128789283
address: 56 2nd street, New York, USA
END
)
(
name: Tom
phone: 6308789283
address: 56 5th street, Chicago, 13611, USA
END
)
现在两者都被分成两个元组。