我有一个输入文件,其中包含数百万条记录,每条记录又包含数千列,其中每一列用分隔符分隔。
记录和列的数量因文件而异。
我有一个要求,我必须解析这些记录并将它们存储在java对象中,以便它可以进一步传递给drools框架进行列级验证。
这就是我的输入数据和模式文件的样子。
输入文件:
John|Doe|35|10 Floyd St|132|Los Angeles|CA|USA ... and so on
...
...
Millions records like this
架构文件:
firstName|String|false|20|NA
lastName|String|false|20|NA
age|Integer|false|3|NA
addressLine1|String|false|20|NA
addressLine2|String|false|20|NA
city|String|false|5|NA
state|String|false|10|NA
country|String|false|10|NA
我试图借助一个Map来实现这个解决方案,并创建了一个包含这个Map的java类。
class GenericRecord {
Map<String,FieldSpecification> properties; //used HashMap as an implementation
}
class FieldSpecification {
public String fieldName;
public String dataType;
public int length;
public String value;
public String format;
}
对于输入文件中的reach行,我正在创建一个 Record
对象并使用map存储其列的值。除此之外,我还将有关列的元数据存储在 FieldSpecification
对象,如数据类型、长度、格式等。
对于我的输入文件中的几千行来说,它工作得很好,但是一旦行数开始增加,它就开始因为内存问题而中断(正如预期的那样)。它正在创建数百万个对象的Map,其中有数千个键。
我知道这不是解决这类问题的有效方法。
因此,我关心的是基于内存的解决方案是否适用于我的场景,或者我更喜欢基于磁盘的解决方案,比如嵌入式数据库或基于磁盘的Map。
请告知是否有任何其他开源Map实现,我可以使用。
注意:对于文件解析和数据验证,我使用的是hadoop,它运行在一个40节点的集群上。
以下是我的Map器的流程和实现:
作为complete行接收该值,然后将该行传递给java框架,java框架将其转换为相应的generiobject(如上所述),然后将该对象传递给drools框架进行进一步验证。
Map器实现:
public void map(LongWritable key , Text value , Context context) throws IOException, InterruptedException {
//Convert the text value to string i.e line by line comes here
String record = value.toString();
// Develop a drools service that will take record as an input
// and will validate it on the basis of XL sheet provided
workingMemory = knowledgeBase.newStatefulKnowledgeSession();
DroolsObject recordObject = DroolsServiceImpl.validateByRecord(record, fileMetaData, workingMemory);
//Check to validate if the processed record
if(recordObject.isValid) {
context.getCounter(AppCounter.VALID_RECORD).increment(1);
mapperOutputKey.set("A");
mapperOutputValue.set(recordObject.toString());
context.write(mapperOutputKey,mapperOutputValue);
}
else {
context.getCounter(AppCounter.INVALID_RECORD).increment(1);
mapperOutputKey.set("R");
mapperOutputValue.set(recordObject.toStringWithErrors());
context.write(mapperOutputKey,mapperOutputValue);
}
}
2条答案
按热度按时间lc8prwob1#
我建议把数据保存在一个(
byte[][]
)表,并通过行的编号引用行。然后,您可以使用一个按需读取相应字段的光标:垃圾收集器应该很容易地处理这些对象。你只需要关心它们的生命周期。
当字节数组不适合你的内存时,好吧,那你就完了。
然后可以通过将名称Map到行号来实现Map。
p8ekf7hl2#
因为您必须将文件中的每个字节的数据都保存在内存中(可能除了分隔符),所以首先要查看文件的大小并将其与内存大小进行比较。如果你的文件比内存大,那就把它保存在内存中的整个想法划掉。
如果内存比文件大,你就有机会了,尽管你需要仔细研究这个文件将来会如何增长,程序将在什么平台上运行,等等。
因此,假设它适合,您可以更有效地使用您的数据结构。保存内存的一个简单方法是废弃Map,只需将每条记录保存为一个字符串(在文件中编码)。一个字符串数组应该有最小的开销,不过您需要确保在填充原始数组时不会不断调整其大小。
当数据结构变大时保持简单可以节省大量内存开销。
另外,如果数据很容易放入内存,则可能需要对jvm进行一些调整,以便为其分配足够的内存(使用-xmx更改堆大小),从而使jvm足够大。我希望您使用的是64位平台上的64位jvm。