2013-11-07 4 views
4

Мне нужно вставить огромное количество узлов с отношениями между ними в Neo4j через конечную точку REST API, около 5 тыс. Записей/с (все еще увеличивается).Оптимизация вставки больших объемов в Neo4j с использованием REST

Это будет непрерывная вставка 24x7. Каждой записи может потребоваться создание только одного узла, но для другого может потребоваться создание двух узлов и одного отношения.

Могу ли я улучшить производительность вставок, изменив мою процедуру или изменив настройки Neo4j?

Мой прогресс до сих пор:

1. Я тестировала с Neo4j на некоторое время, но я не мог получить производительность мне нужно было

окно Тест сервера: 24 ядер + 32GB RAM

Neo4j 2.0.0-M06 установлен как автономный сервис.

Запуск моего приложения Java на том же сервере (Neo4j и Java приложения должны работать на своем собственном сервере в будущем, поэтому встроенный режим не может быть использован)

REST API Endpoint.:/Дб/данные/batch (target:/cypher)

Использование индекса схемы, ограничений, MERGE, CREATE UNIQUE.

2. Моя схема:

neo4j-sh (0)$ schema 
==> Indexes 
==> ON :REPLY(created_at) ONLINE        
==> ON :REPLY(ids)   ONLINE (for uniqueness constraint) 
==> ON :REPOST(created_at) ONLINE        
==> ON :REPOST(ids)  ONLINE (for uniqueness constraint) 
==> ON :Post(userId)  ONLINE        
==> ON :Post(postId) ONLINE (for uniqueness constraint) 
==> 
==> Constraints 
==> ON (post:Post) ASSERT post.postId IS UNIQUE 
==> ON (repost:REPOST) ASSERT repost.ids IS UNIQUE 
==> ON (reply:REPLY) ASSERT reply.ids IS UNIQUE 

3. Мои запросы и Cypher JSON запросы

3.1. Когда одна запись требует создания единого узла, описание работы выглядит ниже

{"method" : "POST","to" : "/cypher","body" : {"query" : "MERGE (child:Post {postId:1001, userId:901})"}} 

3.2. Когда одна запись требует двух узлов с одним отношения, которые будут созданы, описание работы выглядит ниже

{"method" : "POST","to" : "/cypher","body" : {"query" : "MERGE (parent:Post {postId:1002, userId:902}) MERGE (child:Post {postId:1003, userId:903}) CREATE UNIQUE parent-[relationship:REPOST {ids:'1002_1003', created_at:'Wed Nov 06 14:06:56 AST 2013' }]->child"}} 

3.3. Я обычно отправляю 100 описаний должностных обязанностей (смешанные 3.1 и 3.2) за каждую партию, которая занимает около 150 ~ 250 мс, чтобы сделать это.

4. Проблемы с производительностью

4,1. Параллелизм:

/db/data/batch (target:/cypher) кажется небезопасным по потоку, протестирован с двумя или более параллельными потоками, которые запустили сервер Neo4j в течение (-ых) минут.

4.2. MERGE со сдерживанием не всегда работает.

При создании двух узлов и одной связи с одним запросом (упомянутый выше в 3.2.), это когда-то работает как шарм; но он когда-то терпит неудачу с исключением CypherExecutionException и говорит, что один из узлов xxxx уже существует с меткой aaaa и свойством «bbbbb» = [ccccc]; из моего понимания, MERGE не вернет никакого исключения, но вернет узел, если он уже существует.

В результате исключения вся партия будет терпеть неудачу и отката назад, что скажется на моей скорости вставки.

Я открыл вопрос в GitHub для этого вопроса, https://github.com/neo4j/neo4j/issues/1428

4,3. CREATE UNIQUE с ограничениями не всегда работает для создания отношений.

Это упоминается в том же выпуске github.

4.4. Производительность:

На самом деле, прежде чем я использовать пакет с шифром, я попытался устаревшую индексацию с get_or_create (/ дб/данные/индекс/узел/Post единственность = get_or_create &/дб/данные/индекс/отношения/XXXXX ? uniqueness = get_or_create)

Из-за природы этих конечных точек конечного индекса (они возвращают местоположение данных в индексе вместо местоположения данных в реальном хранилище данных), поэтому я не мог использовать их в пакете (требуется функция связанного узла, созданного ранее в той же самой партии)

Я знаю, что могу включить auto_indexing и иметь дело с хранилищем данных непосредственно вместо устаревшего inde x, но они упомянули из 2.0.0, индекс схемы рекомендуется по устаревшему индексу, поэтому я решил перейти к подходу индекса пакетной + cypher + schema.

ОДНАКО, с пакетом + cypher, я могу получить только 200 описаний должностных обязанностей в секунду, это было бы намного выше, если бы MERGE с ограничениями всегда работал, скажем, около 600 ~ 800/с, но он все еще намного ниже 5 к/с. Я также пробовал индекс схемы без каких-либо ограничений, но в итоге он даже снизил производительность с точки зрения скорости вставки.

+0

Вы использовали Streaming JSON? http://docs.neo4j.org/chunked/2.0.0-M06/rest-api-streaming.html. Следующая вещь, которая появляется у меня, касается использования устаревших конечных точек индекса. Вы все равно можете ссылаться на создание в другой пакетной операции. Пример. Создайте узел, используя uniquness get/create. Затем следующий элемент в пакете является cypher, который использует 'START n = node: IndexName (Key = {value}) ....'. Поэтому вы не можете ссылаться на него с помощью batchid, но вы все равно можете воспользоваться частью get/create. – LameCoder

+0

Спасибо, LameCoder! Да, я посмотрел на потоковое видео и подумал, что это больше для восстановления с сервера, а не для отправки на сервер, поэтому я не пробовал, но сейчас я попробую. Кроме того, спасибо за советы по устаревшему индексу. :) – GoSkyLine

+0

Спасибо @ plasmid87 за исправление моей орфографии, грамматики и форматирования. – GoSkyLine

ответ

5

С 2.0 я бы использовал транзакционную конечную точку для создания ваших заявлений партиями, например. 100 или 1000 за HTTP-запрос и около 30 тыс. -50 тыс. За транзакцию (пока вы не совершаете).

Смотрите это для формата нового потокового, транзакционный конечной точки:

http://docs.neo4j.org/chunked/milestone/rest-api-transactional.html

Кроме того, для такой высокой производительности, непрерывное введение конечной Я настоятельно рекомендую писать расширение сервера, который будет работать против встроенного API и может легко вставить 10k или более узлов и связей в секунду, смотрите здесь документации:

http://docs.neo4j.org/chunked/milestone/server-unmanaged-extensions.html

для чистого вставки вам не нужен Cypher. И для параллелизма просто возьмите блокировку на хорошо известном (на каждый подграф, который вы вставляете), так что параллельные вставки не являются проблемой, вы можете сделать это с помощью tx.acquireWriteLock() или удалив несуществующее свойство из узла (REMOVE n.__lock__).

Для другого примера написания неуправляемого расширения (но использующего cypher), проверьте этот проект. Он даже имеет режим, который может помочь вам (POSTing CSV-файлы на конечную точку сервера, которые будут выполняться с использованием оператора cypher для каждой строки).

https://github.com/jexp/cypher-rs

+0

WOW, отлично! Я попробую их. Благодарим вас за все ваши предложения. Я прочитал сообщение в блоге - интервью для вашей лаборатории Streaming REST API, и для меня стало сюрпризом получать удовольствие от помощи. :) – GoSkyLine

+0

Следуя вашему предложению, я создал расширение сервера (используя Java Core API), который выполнил около 1000 записей/1000 ~ 1200 мс (размер партии: 1000, одна транзакция за пакет, один запрос вызова за раз). Я вижу улучшение, я чувствую, что направляюсь в правильном направлении, однако мне нужна более высокая производительность. Для параллелизма, если я посылаю два запроса одновременно, я получаю случайный NPE. [GitHub Issue] (https://github.com/neo4j/neo4j/issues/1561), @Michael Hunger, есть ли у вас какие-либо другие предложения? Спасибо! – GoSkyLine

Смежные вопросы