我有一个包含数百万条记录的输入文件,每条记录又包含数千列,其中每列都用分隔符分隔。
记录和列数可能因文件而异。
我要求我必须解析这些记录并将它们存储在java对象中,以便可以进一步传递给Drools Framework进行列级验证。
这就是我的输入数据和模式文件的样子。
输入文件:
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
我尝试在地图的帮助下实现此解决方案,并创建了一个包含此地图的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;
}
对于输入文件中的覆盖行,我创建了一个Record
对象,并使用map来存储其列的值。除此之外,我还在FieldSpecification
对象中存储有关列的元数据,如dataType,length,format等。
对于我的输入文件中的几千行,它工作正常但是一旦行数开始增加,它就会因为内存问题(正如预期的那样)开始消失。因为它正在创建数百万个具有数千个键的对象映射。
我知道这不是解决这类问题的有效解决方案。
所以我担心基于内存的解决方案在我的场景中是否有效,或者我不得不选择基于磁盘的解决方案,如嵌入式数据库或基于磁盘的地图。
请告知我是否可以使用任何其他开源地图实施。
注意:对于文件分析和数据验证我使用的是hadoop,它运行在40个节点的集群上。
以下是我的映射器的流程和实现:
将值作为完整行接收,稍后将此行传递给Java框架,该框架将其转换为相应的GenericObject(如上所述),然后将此对象传递给drools框架以进行进一步验证。
Mapper实施:
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);
}
}
答案 0 :(得分:1)
由于您必须将文件中的每个字节数据保存在内存中(除了可能的分隔符),首先要查看文件的大小并将其与内存大小进行比较。如果你的文件比内存大,那就抓住将它保存在内存中的全部想法。
如果内存大于文件,你有机会,虽然你需要仔细检查这个文件将来如何增长,程序运行的平台等等。
所以确定它适合,您可以更有效地使用数据结构。一种节省内存的简单方法是废弃地图并将每条记录保存为字符串(在文件中编码)。字符串数组应该具有最小的开销,但是您希望确保在填充原始数组时不会不断调整原始数组的大小。
当数据结构变大时保持数据结构的简单性可以为开销节省大量内存。
此外,如果数据很容易适应内存,您可能需要对JVM进行一些调整,以便为其分配足够的内存(更改堆大小using -Xmx)以使JVM足够大。我希望您在64位平台上使用64位JVM。
答案 1 :(得分:0)
我建议将数据保存在一个(byte[][]
)表中,并通过它们的编号引用行。然后,您可以使用按需读取相应字段的游标:
class FieldSpecification {
private final int row;
private final byte[][] mem;
public String fieldName();
public String dataType();
public int length();
public String value();
public String format();
}
垃圾收集器应该很容易处理这些对象。你只需要关心他们的生命周期。
当字节数组不适合你的记忆时,那么你无论如何都要搞砸了。
然后,您可以通过将名称映射到行号来实现地图。