2017-06-10 22 views
2

私は、それらをneo4j組み込みDBにインポートするためにMySQL/Mariaデータベースから300Kレコードをロードする必要があるJavaアプリケーションを持っています。必要なフィールドをすべて取得するには、4つのテーブルを結合する必要があります。それらのそれぞれは、1:1と他のものとの関係に一致するほぼ300kのレコードを有する。Neo4j OutOfMemoryエラー:GCオーバーヘッドの上限を超えました

これはコードです:

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) 

私は外を追加したときに、この例外は思い付いtable3table4に参加してください。これらの新しい結合の前にエラーはありませんでした。

データを処理しているときにアプリが2GBのRAMと100%のCPUを消費することがわかりました。それが2GBのRAMに達すると、メモリを使い果たします。

私はthis answerと書いてあります。

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.

だから私もちょうどアプリ高いRAMを与えることができるが、これは一種の回避策のようですので、私が代わりに問題を解決したい:コメント欄では、見つけることができます。

私はまた、VisualVMのでアプリをプロファイリングしようと、これは結果であった: enter image description hereenter image description here

それはのNeo4jは、メモリのオーバーヘッドを避けるために、私は一度に10Kのノードを処理する場合でも、メモリ内のすべてのノードを保っているようです。

このようなことをやめる方法を教えてください。

どのようにしてメモリの問題を解決できますか?メモリの問題を解決するために

+0

https://neo4j.com/docs/operations-manual/current/performance/を読み、[ページのキャッシュサイズ](https://neo4j.com/docs/operations-manual/を削減しようcurrent/reference/configuration-settings /#config_dbms.memory.pagecache.size)を参照してください。 – Leon

答えて

0

私はあなたにあなたのアプローチを変更するお勧め:CSVファイルへのあなたのMySQL/MariaDB select文の結果セットをエクスポートし、

  • まず。
  • その後、LOAD CSV句を使用して、CSVファイルをNeo4jデータベースにインポートします。 LOAD CSVを使用する場合は、定期コミットのレートをUSING PERIODIC COMMITで設定できます。 the docsから

:このような

If the CSV file contains a significant number of rows (approaching hundreds of thousands or millions), USING PERIODIC COMMIT can be used to instruct Neo4j to perform a commit after a number of rows. This reduces the memory overhead of the transaction state. By default, the commit will happen every 1000 rows.

ベースインポート・スクリプトますルックス:

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}) 

メモリの問題は、低い値に定期的にコミット率を下げてみてください解決しない場合。

0

どのようにアプリケーションを起動しますか?これは組み込みですか、サーバーエクステンションかプロシージャですか?

後者の場合、内部のバッチが機能しない外部Neo4jトランザクションがあります。

アプリケーションのヒープ設定とページキャッシュ設定は何ですか?

Neo4jビットなしでクエリを実行するとどうなりますか?

ここでDETACH DELETEを使用できますが、結果を閉じてリソースを解放する必要があります。また、ここにあるリレーションシップの数によっては、トランザクション内のレコード数が大幅に増加する可能性があるため、バッチサイズを小さくする必要があります。

graphDB.execute(" MATCH (p:NODELABEL {field1:{field1}}) DETACH DELETE p", params).close(); 
関連する問題