Ich habe eine Java-Anwendung, die 300K Datensatz aus einer MySQL/Maria-Datenbank laden muss, um sie in eine neo4j eingebettete Datenbank zu importieren. Um alle erforderlichen Felder zu erhalten, muss ich 4 Tabellen verbinden. Jeder von ihnen hat fast 300k Datensätze, die mit 1:1
Beziehung mit den anderen übereinstimmen.Neo4j OutOfMemory Fehler: GC Overhead Limit überschritten
Dies ist der Code:
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
}
Alles geht in Ordnung, aber der Import stoppt bei 300k, wartet ca. 5 Sekunden und wirft ein 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)
Diese Ausnahme kam, als ich äußere hinzugefügt beitreten für und table4
. Vor diesen neuen Joins gab es keine Fehler.
Ich habe versucht, die Codeüberwachung Ressourcenverbrauch auf meinem PC erneut auszuführen, stellt sich heraus, dass die App bis zu 2 GB RAM und 100% CPU während der Verarbeitung von Daten nimmt. Wenn es 2 GB RAM erreicht, geht es aus dem Speicher.
Ich habe this answer gelesen. In den Kommentaren finden Sie:
Tim: Would it be correct to summarise your answer as follows: "It's just like an 'Out of Java Heap space' error. Give it more memory with -Xmx." ?
OP: @Tim: No, that wouldn't be correct. While giving it more memory could reduce the problem, you should also look at your code and see why it produces that amount of garbage and why your code skims just below the "out of memory" mark. It's often a sign of broken code.
So konnte ich auch nur die App höher RAM geben, aber diese Art der eine Abhilfe zu sein scheint, so möchte ich das Problem, anstatt zu lösen.
Ich habe auch versucht die App mit VisualVM Profilierung und das war das Ergebnis:
Es scheint, dass Neo4j ist, alle Knoten im Speicher zu halten, auch wenn ich 10K Knoten zur Zeit verarbeiten Speicher-Overhead zu vermeiden.
Wie kann man so etwas verhindern?
Wie kann ich das Speicherproblem lösen?
Lesen https://neo4j.com/docs/operations-manual/current/performance/ und versuchen, die [Seite Cache-Größe] zu reduzieren (https://neo4j.com/docs/operations-manual/ current/reference/Konfigurationseinstellungen/# config_dbms.memory.pagecache.size). – Leon