簡體   English   中英

Neo4j OutOfMemory錯誤:超出了GC開銷限制

[英]Neo4j OutOfMemory Error: GC overhead limit exceeded

我有一個Java應用程序,需要從MySQL / Maria數據庫加載300K記錄,以便將它們導入neo4j嵌入式數據庫。 要獲取所有必填字段,我需要加入4個表。 他們每個人都有將近30萬條記錄,彼此之間以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)

當我為table3table4添加外部table3時出現此異常。 在進行這些新連接之前,沒有任何錯誤。

我嘗試在計算機上重新執行代碼監視資源使用情況,結果發現該應用在處理數據時需要占用2GB RAM和100%CPU。 當它達到2GB RAM時,內存將耗盡。

我已經讀過這個答案 在評論部分中,您可以找到:

蒂姆:將您的答案總結如下是正確的:“就像一個'Java堆空間不足'錯誤。使用-Xmx為它提供更多的內存。”

OP:@Tim:不,那是不正確的。 雖然為它提供更多的內存可以減少問題,但您還應該查看代碼,看看為什么它會產生那么多的垃圾,以及為什么代碼在“內存不足”標記的下方略讀。 這通常是代碼損壞的跡象。

因此,我也可以給應用程序提供更高的RAM,但這似乎是一種解決方法,所以我想解決此問題。

我還嘗試使用VisualVM對應用程序進行性能分析,結果是: 在此處輸入圖片說明 在此處輸入圖片說明

似乎neo4j會將所有節點都保留在內存中,即使我一次處理10K節點也要避免內存開銷。

如何阻止它這樣做?

如何解決內存問題?

為了解決內存問題,我建議您將方法更改為:

  • 首先,將MySQL / MariaDB select語句的結果集導出到CSV文件。
  • 之后,使用LOAD CSV子句將CSV文件導入到Neo4j數據庫中。 使用LOAD CSV ,可以USING PERIODIC COMMIT設置定期提交的速率。

文檔

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

如果內存問題仍然存在,請嘗試將定期提交率降低到較低的值。

您如何啟動您的應用程序? 這是嵌入式的還是服務器擴展或過程?

如果是后者,則有一個外部Neo4j事務正在使您的內部批處理不起作用。

應用程序的堆配置和頁面緩存配置是什么?

如果不使用Neo4j位執行查詢會怎樣?

您可以在此處使用DETACH DELETE ,但應關閉結果以釋放資源。 同樣,根據您在這里有多少關系,事務中的記錄數可能會大大增加,因此您可能需要減小批處理大小。

graphDB.execute(" MATCH (p:NODELABEL {field1:{field1}}) DETACH DELETE p", params).close();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM