我有一个java应用程序需要从MySQL / Maria数据库加载300K记录,以便将它们导入neo4j嵌入式数据库。要获得所有必需的字段,我需要加入4个表。他们每个人都有近300k的记录,与其他人的1:1
关系相匹配。
这是代码:
String query = ""
+ "SELECT "
+ " a.field1, "
+ " a.field2, "
+ " a.field3, "
+ " f.field4, "
+ " a.field5, "
+ " a.field6, "
+ " a.field7, "
+ " a.field8, "
+ " a.field9, "
+ " a.field10, "
+ " b.field11, "
+ " b.field12, "
+ " b.field13, "
+ " l.field14, "
+ " l.field15, "
+ " a.field16 "
+ "FROM table1 a "
+ "LEFT OUTER JOIN table2 f ON f.pkTable2 = a.fkTable2 "
+ "LEFT OUTER JOIN table3 b ON b.pkTable3 = a.fkTable3 "
+ "LEFT OUTER JOIN table4 l ON l.pk1Table4 = a.fk1Table4 AND l.pk2Table4 = a.fk2Table4 ";
try (
Connection connection = ds.getConnection();
PreparedStatement statement = connection.prepareStatement(query);
ResultSet rs = statement.executeQuery();
) {
Transaction tx = graphDB.beginTx(); // open neo4j transaction
int count = 0;
int count = 0;
rs.setFetchSize(10000);
while(rs.next()) {
String field1 = rs.getString("field1");
String field2 = rs.getString("field2");
String field3 = rs.getString("field3");
String field4 = rs.getString("field4");
String field5 = rs.getString("field5");
String field6 = rs.getString("field6");
String field7 = rs.getString("field7");
String field8 = rs.getString("field8");
String field9 = rs.getString("field9");
String field10 = rs.getString("field10"); // <-- error comes here
String field11 = rs.getString("field11");
String field12 = rs.getString("field12");
String field13 = rs.getString("field13");
String field14 = rs.getString("field14");
String field15 = rs.getBigDecimal("field15");
String field16 = rs.getBigDecimal("field16");
// process data - insert/update/delete in neo4j embedded DB
if("D".equals(field16)) { // record deleted in mysql db - delete from neo4j too
Map<String, Object> params = new HashMap<String, Object>();
params.put("field1", field1);
graphDB.execute(" MATCH (p:NODELABEL {field1:{field1}}) OPTIONAL MATCH (p)-[r]-() DELETE r,p", params);
} else {
Node node;
if("M".equals(field16)) { // record modified, load the existing node and edit it
node = graphDB.findNode(Labels.NODELABEL, "field1", field1);
} else { // new record, create node from scratch
node = graphDB.createNode(Labels.NODELABEL);
}
node.setProperty("field1", field1);
node.setProperty("field2", field2);
node.setProperty("field3", field3);
node.setProperty("field4", field4);
node.setProperty("field5", field5);
node.setProperty("field6", field6);
node.setProperty("field7", field7);
node.setProperty("field8", field8);
node.setProperty("field9", field9);
node.setProperty("field10", field10);
node.setProperty("field11", field11);
node.setProperty("field12", field12);
node.setProperty("field13", field13);
node.setProperty("field14", field14);
node.setProperty("field15", field15);
}
count++;
if(count % 10000 == 0) {
LOG.debug("Processed " + count + " records.");
tx.success(); // commit
tx.close(); // close neo4j transaction (should free the memory)
tx = graphDB.beginTx(); // reopen the transaction
}
}
// commit remaining records and close the last transaction
tx.success();
tx.close();
} catch (SQLException ex) {
// LOG exception
}
一切顺利但导入停止在300k,等待大约5秒并抛出OutOfMemoryException
:
java.lang.OutOfMemoryError: GC overhead limit exceeded
at com.mysql.cj.core.util.StringUtils.toString(StringUtils.java:1665)
at com.mysql.cj.core.io.StringValueFactory.createFromBytes(StringValueFactory.java:93)
at com.mysql.cj.core.io.StringValueFactory.createFromBytes(StringValueFactory.java:36)
at com.mysql.cj.core.io.MysqlTextValueDecoder.decodeByteArray(MysqlTextValueDecoder.java:232)
at com.mysql.cj.mysqla.result.AbstractResultsetRow.decodeAndCreateReturnValue(AbstractResultsetRow.java:124)
at com.mysql.cj.mysqla.result.AbstractResultsetRow.getValueFromBytes(AbstractResultsetRow.java:225)
at com.mysql.cj.mysqla.result.ByteArrayRow.getValue(ByteArrayRow.java:84)
at com.mysql.cj.jdbc.result.ResultSetImpl.getString(ResultSetImpl.java:880)
at com.mysql.cj.jdbc.result.ResultSetImpl.getString(ResultSetImpl.java:892)
at org.apache.tomcat.dbcp.dbcp2.DelegatingResultSet.getString(DelegatingResultSet.java:266)
at org.apache.tomcat.dbcp.dbcp2.DelegatingResultSet.getString(DelegatingResultSet.java:266)
at com.js.Importer.importData(Importer.java:99)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
当我为table3
和table4
添加外部联接时,出现了此异常。在这些新的连接之前没有错误。
我尝试在我的电脑上重新执行代码监控资源使用情况,结果发现应用程序在处理数据时需要2GB RAM和100%CPU。当它达到2GB RAM时,内存不足。
我看过this answer。在评论部分,您可以找到:
Tim:总结一下你的答案是否正确如下:“这就像'出于Java堆空间'错误。用-Xmx为它提供更多内存。” ?OP:@Tim:不,那不正确。虽然给它更多的内存可以减少问题,你还应该查看你的代码,看看为什么它产生了大量的垃圾以及为什么你的代码在“内存不足”标记之下掠过。它通常是代码破坏的标志。
所以我也可以给应用程序提供更高的内存,但这似乎是一种解决方法,所以我想解决这个问题。
似乎neo4j将所有节点保留在内存中,即使我及时处理10K节点以避免内存开销。
如何阻止它做这样的事情?
如何解决内存问题?
答案 0 :(得分:0)
要解决内存问题,我建议您改变方法:
LOAD CSV
时,您可以使用USING PERIODIC COMMIT
设置定期提交的比率。来自the docs:
如果CSV文件包含大量行(接近 数十万或数百万),
USING PERIODIC COMMIT
可以使用 指示Neo4j在多行后执行提交。 :此 减少事务状态的内存开销。默认情况下 提交将每1000行发生一次。
基本导入脚本如下所示:
USING PERIODIC COMMIT 10000 // Commit after 10000 rows
LOAD CSV FROM 'path/to/csv/file.csv' AS line
// you can use line.field1 to access field1 property
// your Cypher statements go here, for example
CREATE (:Node { field1: line.field1})
如果内存问题仍然存在,请尝试将定期提交速率降低到较低值。
答案 1 :(得分:0)
您如何开始申请?这是嵌入式还是服务器扩展或程序?
如果是后者,则会有一个外部Neo4j交易,使您的内部批次无效。
应用程序的堆配置和页面缓存配置是什么?
如果在没有Neo4j位的情况下执行查询会发生什么?
您可以在此处使用DETACH DELETE
,但应关闭结果以释放资源。此外,根据您在此处拥有的关系数量,您的交易中的记录数量可能会大幅增加,因此您可能需要减少批量大小。
graphDB.execute(" MATCH (p:NODELABEL {field1:{field1}}) DETACH DELETE p", params).close();