Загрузка 9M строк из Neo4j и запись их в CSV вызывает исключение из памяти.

У меня есть большая модель графа, и мне нужно записать результат следующего запроса в csv.

Match (u:USER)-[r:PURCHASED]->(o:ORDER)-[h:HAS]->(i:ITEM) return u.id as userId,i.product_number as itemId

Когда я запрашиваю «Объяснение», я получаю следующий результат:  введите описание изображения здесь

Это показывает, что расчетный результат составляет около 9 млн. Мои проблемы:

1) На получение ответа уходит много времени. Из neo4j-shell это занимает 38 минут! Это нормально? Кстати, у меня есть все индексы схемы, и все они ОНЛАЙН.

2) Когда я использую SpringDataNeo4j для получения результата, он выдает ошибку «java.lang.OutOfMemoryError: превышен предел накладных расходов GC», и это происходит, когда SDN пытается преобразовать загруженные данные в наш объект @QueryResult.

Я пробовал оптимизировать запрос разными способами, но ничего не изменилось! У меня такое впечатление, что я что-то делаю не так. Кто-нибудь знает, как я могу решить эту проблему? Следует ли мне использовать пакетное чтение / запись?

PS Я использую версию сообщества Neo4j версии: 3.0.1, и это мои sysinfos: введите описание изображения здесь

а это мои конфиги сервера.

dbms.jvm.additional=-Dunsupported.dbms.udc.source=tarball
use_memory_mapped_buffers=true
neostore.nodestore.db.mapped_memory=3G
neostore.relationshipstore.db.mapped_memory=4G
neostore.propertystore.db.mapped_memory=3G
neostore.propertystore.db.strings.mapped_memory=1000M
neostore.propertystore.db.index.keys.mapped_memory=500M
neostore.propertystore.db.index.mapped_memory=500M

person Lina    schedule 16.11.2016    source источник


Ответы (3)


Хотя Neo4j будет передавать вам результаты по мере их совпадения, при использовании SDN он должен собирать выходные данные в один объект @QueryResult. Чтобы избежать проблем с OOM, вам необходимо либо убедиться, что ваше приложение имеет достаточно памяти кучи, доступной для загрузки всех 9-метровых ответов, либо использовать оболочку neo4j, либо использовать специальный потоковый интерфейс, такой как https://www.npmjs.com/package/cypher-stream. (caveat emptor: я не пробовал, но похоже, что это должно помочь)

person Vince    schedule 16.11.2016
comment
Спасибо, Винс, ты намекнул мне о @QueryResult! - person Lina; 17.11.2016

Ваши настройки конфигурации неверны для Neo4j 3.0.1

вам нужно установить кучу в conf / neo4j-wrapper.conf, например 8G

и page-cache в conf / neo4j.conf (глядя на ваш магазин, вам нужно только 2G для page-cache).

Также, как вы можете видеть, он создаст более 8 миллионов строк.

Возможно, вам повезет больше с этим запросом:

Match (u:USER)-[:PURCHASED]->(:ORDER)-[:HAS]->(i:ITEM) 
with distinct u,i
return u.id as userId,i.product_number as itemId

Также нет смысла возвращать 8M строк в neoj-shell, если честно. Если вы хотите измерить его, замените RETURN на WITH и добавьте RETURN count(*)

Match (u:USER)-[r:PURCHASED]->(o:ORDER)-[h:HAS]->(i:ITEM) 
with distinct u,i
WITH u.id as userId,i.product_number as itemId
RETURN count(*)

Другая оптимизация может заключаться в том, чтобы пройти через элемент и пользователя и выполнить хеш-соединение посередине для глобального запроса, например:

Match (u:USER)-[:PURCHASED]->(o:ORDER)-[:HAS]->(i:ITEM) 
USING JOIN ON o
with distinct u,i
WITH u.id as userId,i.product_number as itemId
RETURN count(*)

Еще одна вещь, которую я, вероятно, сделал бы, чтобы уменьшить количество возвращаемых результатов, - это попытаться агрегировать.

Match (u:USER)-[:PURCHASED]->(o:ORDER)-[:HAS]->(i:ITEM)
with distinct u,i
WITH u, collect(distinct i) as products
WITH u.id as userId,[i in products | i.product_number] as items
RETURN count(*)
person Michael Hunger    schedule 16.11.2016
comment
Спасибо, Майкл, вы правы, dbms.memory.pagecache.size был установлен в неправильном файле. Я изменил это. Что касается количества возвращаемых результатов, то я не хочу их сокращать, они мне нужны все. Мне нужно создать csv userId, itemId ... Не могу использовать какую-либо группировку ... Я пробовал все предложенные вами запросы (vi neo4j-shell ...) все имели одинаковое время ответа. Я думаю, что мои данные не совсем нормализованы :-), но мне нужны все дубликаты. - person Lina; 17.11.2016

Благодаря комментариям Винса и Майкла я нашел решение! Проведя несколько экспериментов, выяснилось, что время отклика сервера на самом деле хорошее! 1,5 минуты на 9 миллионов данных! Проблема связана с SDN, как упоминал Винс! OOM происходит, когда SDN пытается преобразовать данные в объект @QueryResult. Увеличение памяти кучи для нашего приложения не является постоянным решением, так как в будущем у нас будет больше строк! Поэтому мы решили использовать neo4j-jdbc-driver для запросов больших данных ... и он работает как самолет! Вот пример кода, который мы использовали:

Class.forName("org.neo4j.jdbc.Driver");
    try (Connection con = DriverManager.getConnection("jdbc:neo4j:bolt://HOST:PORT", "USER", "PASSWORD")) {

        // Querying
        String query = "match (u:USER)-[r:PURCHASED]->(o:ORDER)-[h:HAS]->(i:ITEM) return u.id as userId,i.product_number as itemId";
        con.setAutoCommit(false); // important for large dataset
        Statement st = con.createStatement();
        st.setFetchSize(50);// important for large dataset

            try (ResultSet rs = st.executeQuery(query)) {
                while (rs.next()) {
                    writer.write(rs.getInt("userId") + ","+rs.getInt("itemId"));
                    writer.newLine();
                }

            }

        st.setFetchSize(0);
        writer.close();
        st.close();

    }

Просто убедитесь, что вы используете con.setAutoCommit (false); и st.setFetchSize (50), если знаете, что собираетесь загрузить большой набор данных. Спасибо всем !

person Lina    schedule 17.11.2016