我正在开发一个系统,它可以加载一个巨大的CSV文件(超过100万行)并保存到数据库中。每条线也有超过一千个场。 CSV文件被视为一个批处理,每行被视为其子对象。在添加对象的过程中,每个对象都将保存在单个批处理列表中,并且在某些时候我的内存不足,因为List将添加超过100万个对象。我不能将文件拆分为N个数字,因为不是按顺序排列的行之间存在依赖关系(任何行都可以依赖于其他行)。
以下是一般逻辑:
Batch batch = new Batch();
while (csvLine !=null ){
{
String[] values = csvLine.split( ",", -1 );
Transaction txn = new Transaction();
txn.setType(values[0]);
txn.setAmount(values[1]);
/*
There are more than one thousand transaction fields in one line
*/
batch.addTransaction (txn);
}
batch.save();
我们有什么方法可以在服务器内存不足的情况下处理这种情况吗?
答案 0 :(得分:1)
在过去,我们过去常常处理存储在顺序磁带上的大量数据,而内存和磁盘很少。但是花了很多时间!
基本上,您构建的行缓冲区可以容纳在内存中,浏览所有文件以解析依赖关系并完全处理这些行。然后迭代下一个缓冲区,直到处理完所有文件。如果每个缓冲区需要完整读取文件,但允许节省内存。
此处可能存在其他问题,因为您希望将所有记录存储在一个批处理中。批处理将需要足够的内存来存储所有记录,因此再次存在耗尽内存的风险。但是你可以再次使用好的旧方法,并保存许多较小尺寸的批次。
如果您想确保所有内容都完全插入数据库或一切都将被拒绝,您只需使用交易:
专业级数据库(MySQL,PostgreSQL,Oracle等)可以使用磁盘上的回滚段来处理一个事务而不会耗尽内存。当然,它比内存操作慢得多(如果出于任何原因你不得不回滚这样的事务!),但至少它可以工作,除非你耗尽可用的物理磁盘...
答案 1 :(得分:0)
专门为
如果您需要分析java中的CSV字段,通过缓存来限制值实例的数量:
public class SharedStrings {
private Map<String, String> sharedStrings = new HashMap<>();
public String share(String s) {
if (s.length() <= 15) {
String t = sharedStrings.putIfAbsent(s, s); // Since java 8
if (t != null) {
s = t;
}
/*
// Older java:
String t = sharedString.get(s);
if (t == null) {
sharedString.put(s, s);
} else {
s = t;
}
*/
}
return s;
}
}
在你的情况下,对于长记录,它甚至可以将读取行的 GZipOutputStream 作为字节,更短的字节数组。 但是数据库似乎更合乎逻辑。
答案 2 :(得分:0)
如果您正在使用csvLine的所有字段,则以下内容可能不适用。
String#split使用String #substring,而后者不会创建新的字符串,而是将原始字符串保留在内存中并引用相应的部分。
所以这一行会将原始字符串保留在内存中:
String a = "...very long and comma separated";
String[] split = a.split(",");
String b = split[1];
a = null;
因此,如果你没有使用csvLine的所有数据,你应该将每个值的条目包装在一个新的String中,即在上面的例子中你会这样做
String b = new String(split[1]);
否则gc无法释放字符串a。
我遇到了这个,而我正在提取一个包含数百万行的csv文件列。