1

В моем проекте я использую spring-data-neo4j 4.2.0.M1 с neo4j-ogm 2.0.4. Первоначально это использовало встроенный экземпляр neo4j, но в ходе исследования по этой проблеме я перешел на выделенный экземпляр neo4j (работающий на той же машине, хотя), используя протокол Bolt.Neo4j-ogm: Уменьшение производительности записи/сопоставления

Я постоянно вставляю данные, в основном, поскольку он становится доступным для моего приложения (поэтому я не могу использовать пакетную вставку). После запуска это отлично работает и сохранение экземпляра моего NodeEntity занимает ~ 60 мс, что отлично подходит для моего использования. Однако это медленно ухудшается с течением времени. Через 10-20 минут это замедляется примерно до 2 с за сохранение, что уже не так здорово. Время, похоже, здесь достигает максимума и не уменьшается намного больше.

Первоначально я предполагал, что это было вызвано слишком маленьким внедренным экземпляром, так как я видел повторяющиеся сообщения о приостановках GC, сообщаемых neo4j. Затем я перешел на выделенный экземпляр, который намного больше, и эти предупреждения GC больше не отображаются. Однако деградация все же происходит.

размеры магазина, как сообщает Neo4j:

Array Store 8.00 KiB 
Logical Log 151.36 MiB 
Node Store 40.14 MiB 
Property Store 1.83 GiB 
Relationship Store 742.63 MiB 
String Store> Size 120.87 MiB 
Total Store Size 4.55 GiB 

Экземпляр является конфигурирует следующим образом: (! Режим сэмплера)

dbms.memory.pagecache.size=5g 
dbms.memory.heap.initial_size=4g 
dbms.memory.heap.max_size=4g 
dbms.jvm.additional=-XX:+UseG1GC 

Использование YourKit профилировщика я могу видеть, что большую часть времени, кажется, расходоваться EntityGraphMapper Neo4j-OGM, в частности, в

org.neo4j.ogm.context.EntityGraphMapper#haveRelationEndsChanged 

YourKit Profiler

Сохранение NodeEntity обычно имеет около 40 связей с другими узлами, большинство из которых моделируется как RelationshipEntity. На более ранней стадии я уже заметил, что сохранение объектов было довольно медленным, так как были отображены слишком много связанных (но неизменных) объектов. С тех пор я использую глубину 1 при сохранении. Непрерывные операции, которые приводят к сохранению NodeEntitites, используют размер транзакции 200 объектов.

Я еще не уверен, что neo4j-ogm на самом деле является причиной замедления, так как я не вижу, какие изменения по сравнению с хорошими исходными результатами. В таких случаях я обычно подозреваю утечку памяти/загрязнение, но все результаты мониторинга для этого хорошо выглядят в моем приложении. Для экземпляра сервера neo4j я не знаю, где искать такую ​​информацию, кроме debug.log.

В общем, я потратил довольно много времени на изучение этого и не знаю, что еще посмотреть. Любые мысли или предложения? Я рад предоставить дополнительную информацию.

Edit: Follwing @ вход Винса, я был еще раз взглянуть на распределение памяти и обнаружил, что на самом деле Neo4jSession вырос довольно много после того, позволяя запустить приложение в течение ~ 3 часов:

neo4j-ogm-memory

В то время куча была 1,7 ГБ большой, из которых 70% ссылались на живые данные. Из этого в настоящее время упоминается (и поддерживается) около 300 МБ Neo4jSession. Это может означать, что оно стало слишком большим. Как я могу вмешаться вручную здесь?

+0

Вы создаете новый сеанс для каждой транзакции (партия из 200 объектов) или используете один сеанс? – Vince

+0

Я использую ту же сессию (я думаю). У меня нет ручного управления сессиями, а также использование области по умолчанию. Из того, что я понял из документации, это должно быть полезно для многих более длительных операций? Тем не менее, я не ожидаю обновлений за пределами рабочего потока. – geld0r

+2

Существа придерживаются в сессии, пока не получат сбор мусора. Может быть какое-то влияние на производительность в 'hasRelationEndsChanged', если вы загружаете много тысяч объектов, поэтому может стоить делать' session.clear() 'между каждой транзакцией и посмотреть, поможет ли это. – Vince

ответ

1

Субъекты придерживаются в ходе сессии, пока не получат сбор мусора. Может возникнуть некоторое влияние на производительность в haveRelationEndsChanged, если вы загружаете много тысяч объектов, поэтому может стоить делать session.clear() между каждой транзакцией и посмотреть, помогает ли это

1

некоторое время назад у нас практически такая же ситуация, когда нам нужно было хранить большое количество данных в neo4j. Мы проанализировали различные подходы, как справиться с этим.Итак, мы нашли несколько решений, как ускорить вставку данных в neo4j.

  1. Используйте родной драйвер neo4j java вместо данных весны. Прежде всего это async api, и если доступность данных для выбора не является критичной на данный момент, это может помочь.

  2. Используйте транзакции для ввода нескольких записей (например, 1000 вставок на транзакцию). Это ускорит вставку, потому что после любой транзакции commit neo4j пытается пересчитать индексы с lucene и требуется время. В вашем случае (используя пружинные данные) любая вставка выполняется в отдельной транзакции.

+0

Мне бы очень хотелось избежать написания ручных запросов для этой разнообразной задачи импорта. Spring-data-neo4j/neo4j-ogm позволяют немного упростить это. Я буду держать это предложение в виду, хотя для аналогичных случаев, когда обновления, которые будут обработаны, более схожи. – geld0r

2

Надеюсь, что еще не поздно помочь в этой проблеме.

Недавно я столкнулся с такой же ситуацией, когда сохранял узел с ~ 900 отношениями внутри Set и мог заставить его выполнить от ~ 5 секунд до 500 мс. Первоначально я использовал neo4j-ogm 2.1.3 и теперь перешел на 3.0.0. Несмотря на то, что 3.0.0 намного быстрее, прирост производительности был одинаковым в обеих версиях.

Вот некоторые псевдо-код (я не могу разделить реальный код сейчас):

@NodeEntity(label = "MyNode") 
public class MyNode { 
    @GraphId 
    private Long id; 

    @Index(unique = true, primary = true) 
    private String myUniqueValue; 

    private String value; 

    @Relationship(type = "CONNECTS_TO") 
    private Set<MyRelationship> relationships; 
    // constructors, getters, setters 
} 

@Relationship(type = "CONNECTS_TO") 
public class MyRelationship { 

    @GraphId 
    private Long id; 

    @StartNode 
    private MyNode parent; 

    @EndNode 
    private MyNode child; 
    // constructors, getters, setters 
} 

Обратите внимание, что MyNode имеет индексный/уникальное поле, где у меня есть полный контроль над значением. neo4j-ogm будет использовать его для определения того, должен ли он выполнять оператор CREATE или MERGE. В моем случае использования, я хочу, чтобы слияние произошло, если узел уже существует.

Создание отношений, с другой стороны, зависит от идентификатора узла (@GraphId). Вот небольшой отрывок из заявления генерироваться, что создает его:

UNWIND {rows} as row MATCH (startNode) WHERE ID(startNode) = row.startNodeId MATCH (endNode) WHERE ID(endNode) = row.endNodeId...

В медленном режиме, Neo4j-ОГМЫ будут заботиться о проверке ли отношения или узлы в ней уже сохранены и будут получать идентификаторы необходимо создать узел. Это операция, которую вы захватили в YourKit.

Пример, который выполняет медленно:

void slowMode() { 
    MyNode parent = new MyNode("indexed-and-unique", "some value"); 
    for (int j = 0; j < 900; j++) { 
     MyNode child = new MyNode("indexed-and-unique" + j, "child value" + j); 
     parent.addRelationship(new MyRelationship(parent, child)); 
    } 
    session.save(parent); // save everything. slow. 
} 

Решение, которое я нашел в том, чтобы разбить эти операции на три части:

  • Сохранить родительский узел только

  • Сохранить дочерние узлы

  • Сохранить рела tionships

Это намного быстрее:

void fastMode() { 
    MyNode parent = new MyNode("indexed-and-unique", "some value"); 
    for (int j = 0; j < 900; j++) { 
     MyNode child = new MyNode("indexed-and-unique" + j, "child value" + j); 
     parent.addRelationship(new MyRelationship(parent, child)); 
    } 
    session.save(parent, 0); // save only the parent 
    session.save(getAllChildsFrom(parent), 0); // save all the 900 childs 
    // at this point, all instances of MyNode will contain an "id". time to save the relationships! 
    session.save(parent); 
} 

Одна вещь, чтобы обратить внимание: Neo4j-ОГМ 2,1.3 не выполнял одиночный пакетный оператор при сохранении коллекции узлов (session.save(getAllChildsFrom(parent), 0)), которая по-прежнему частая и медленная, но не так медленно, как раньше. Версия 3.0.0 исправляет это.

Надеюсь, это поможет!