Как я сказал вчера, я был занят в последние месяцы производством контента, так что вот, пожалуйста. Для связанной работы мы, скорее всего, будем использовать neo4j в качестве основной базы данных. Это имеет смысл, поскольку мы в основном строим какую-то социальную сеть. Большинство запросов, на которые мы должны ответить, предлагая услугу или во время интеллектуального анализа данных, имеют структуру друзей.
Для некоторых запросов мы выполняем подсчет или агрегирование, поэтому мне было интересно, каков наиболее эффективный способ запроса к базе данных neo4j. Так что я сделал тест с довольно неожиданными результатами.
Просто быстрое замечание: мы использовали базу данных, состоящую из статей и авторов, извлеченных из arxiv.org, одного из крупнейших пре-печатных сайтов, доступных в Интернете. Набор данных доступен для загрузки и воспроизведения результатов тестов по адресу http://blog.related-work.net/data/
База данных в виде файла neo4j имеет размер 2 ГБ (в сжатом виде), схема выглядит примерно так:
Paper1 <--[ref]--> Paper2 | | |[author] |[author] v v Author1 Author2
Для эталонного теста мы пытались найти соавторов, которые в основном являются друзьями по запросу друга после отношений с автором (или поиска в ширину (глубина 2))
Как мы знаем, существует три основных способа общения с базой данных neo4j:
Java Core API
Здесь вы работаете с узлами и объектами отношений в Java. Формулировка запроса после исправления узла автора выглядит примерно так.
for (Relationship rel: author.getRelationships(RelationshipTypes.AUTHOROF)){ Node paper = rel.getOtherNode(author); for (Relationship coAuthorRel: paper.getRelationships(RelationshipTypes.AUTHOROF)){ Node coAuthor = coAuthorRel.getOtherNode(paper); if (coAuthor.getId()==author.getId())continue; resCnt++; } }
Мы видим, что код может выглядеть очень запутанным ( если запросы становятся более сложными ). С другой стороны, можно легко объединить несколько похожих переходов в один большой запрос, что ухудшает читабельность, но повышает производительность.
Traverser Framework
Traverser Framework поставляется с Java API, и мне очень нравится его идея. Я думаю, что действительно легко понять значение запроса, и, на мой взгляд, это действительно помогает создать хорошую читабельность кода.
Traversal t = new Traversal(); for (Path p:t.description().breadthFirst(). relationships(RelationshipTypes.AUTHOROF).evaluator(Evaluators.atDepth(2)). uniqueness(Uniqueness.NONE).traverse(author)){ Node coAuthor = p.endNode(); resCnt++; }
Особенно, если у вас много похожих запросов или запросов, которые являются уточнениями других запросов, вы можете сохранить их и расширить их с помощью Traverser Framework. Какая классная техника.
Cypher Query Language
И еще есть язык Cypher Query. Интерфейс очень сильно подтолкнул neo4j. Если вы посмотрите на запрос, вы сможете полностью понять, почему. Это действительно красивый язык, близкий к SQL (Глядя на Stackoverflow, на самом деле страшно, как много людей пытаются отвечать на запросы Foaf с использованием MySQL), но все же акцентирует внимание на графоподобной структуре.
ExecutionEngine engine = new ExecutionEngine( graphDB ); String query = "START author=node("+author.getId()+ ") MATCH author-[:"+RelationshipTypes.AUTHOROF.name()+ "]-()-[:"+RelationshipTypes.AUTHOROF.name()+ "]- coAuthor RETURN coAuthor"; ExecutionResult result = engine.execute( query); scala.collection.Iterator<Node> it = result.columnAs("coAuthor"); while (it.hasNext()){ Node coAuthor = it.next(); resCnt++; }
Я всегда интересовался производительностью этого языка запросов. Написание языка запросов очень сложная задача, и чем выразительнее язык, тем сложнее добиться хорошей производительности (то же самое относится и к SPARQL в семантической сети). И давайте просто отметим, что Сайфер довольно выразителен.
Что, где результаты?
- Базовый API может отвечать на около 2000 запросов друга друга (я должен признать, в очень редкой сети).
- Платформа Traverser работает примерно на 25% медленнее, чем Core API
- Наихудшим является шифр, который медленнее, по крайней мере, на один порядок, способный отвечать примерно на 100 запросов FOAF в секунду.
Я был шокирован, поэтому поговорил с Андресом Тейлором из neo4j, который в основном работает на Cypher. Он спросил, какую версию neo4j я использовал, и я ответил, что это 1.7. Он сказал мне, что я должен проверить 1.9. с тех пор как Cypher стал более производительным. Поэтому я запускаю тесты над neo4j 1.8 и neo4j 1.9, к сожалению, Cypher стал медленнее в новых версиях neo4j.
Цитаты из Андрес Тейлор:
Сайферу чуть больше года. Поскольку мы очень ограничены в работе с разработчиками, мы должны были быть очень разборчивы в том, что мы работаем над тем, чтобы сосредоточиться на этом первом этапе, чтобы изучить язык и узнать, как наши пользователи используют язык запросов, и расширить набор функций. до разумного уровня
Я считаю, что Cypher — наш будущий API. Я знаю, что вы можете очень легко превзойти Cypher по почерку запросов. Как и любой язык, когда-либо созданный, в начале вы всегда можете сделать лучше, чем компилятор, написав от руки, но в конце концов, компилятор догоняет
Вывод:
До сих пор я использовал только Java Core API для работы с neo4j, и я буду продолжать это делать.
Если вы находитесь в высокоскоростном сценарии (я полагаю, что каждое веб-приложение одно), вам следует подумать о переходе на API ядра Java neo4j для написания ваших запросов. Это может быть не так красиво, как Cypher или traverser Framework, но выигрыш в скорости окупается.
Также мне лично нравится уровень контроля, который ты имеешь, проходя через ядро самостоятельно.
Вскоре я опубликую статью, почему языки сценариев, такие как PHP, Python или Ruby, в любом случае не подходят для создания веб-приложений. Поэтому переход к основному API имеет смысл даже по нескольким причинам.
Полный исходный код теста можно найти по адресу https://github.com/renepickhardt/related-work.net/blob/master/RelatedWorkBrowser/src/de/renepickhardt/gwt/server/neo4jHelper/benchmarks/FriendOfAFriendQueryava.en (commit: 0d73a2e6fc41177f3249f773f7e96278c1b56610)
Подробные результаты можно найти в этой таблице.