Pull to refresh

Comments 35

Удивительные результаты!
В первых экспериментах на поиск время растет логарифмически, это я еще могу как-то понять. Но в последнем эксперименте такое поведение уже совсем не укладывается в голове.
Кстати, а как выглядела схема для MySQL? Были ли индексы, какие запросы выполнялись?
У Вас есть свое предположение о причинах такого роста времени у Neo4j?
Как только у lucene заканчивается память, то начинаются тормоза.
В целом, ничего революционного в Neo4j по-моему нет. Главной проблемой для всех графовых баз данных является скорость доступа к самому графу, все алгоритмы траверсинга имеют примерно одинаковую производительность независимо от реализации.

В целом, Neo4j удобен Cypher'ом, но он позволяет реализовать только базовый поиск, а для использования более сложных алгоритмов прийдется использовать Java API.
Для реляционной бд дополнительных индексов не создавал. Разные росты времени напрямую зависят от алгоритмов поиска. Здесь нужен более глубокий анализ алгоритмов, чтобы понять причину такого огромного разрыва. Меня лично удивило то, что даже поиск по PK в MySQL на столько хуже.
Вы бы показали что у вас в классах MySQLDAO и Neo4jDAO (это ведь ваши собственные классы?).
Ну, все просто же.

Вынесите
String sql_query = "SELECT * FROM social_network.user " +
                "WHERE social_network.user.id = ?;";
        PreparedStatement ps = conn.prepareStatement(sql_query);


за цикл в идеале туда же где вы определяете Connection, недаром же в javadoc PreparedStatement пишут:

A SQL statement is precompiled and stored in a PreparedStatement object. This object can then be used to efficiently execute this statement multiple times.

И результаты экспериментов неожиданно станут совсем другими. У вас код большую часть времени тупо тратил на парсинг sql запросов и подготовку PreparedStatement на каждый чих.
Это не данные а толпа артефактов, второй эксперимент наглядно показывает как вы сначала прогреваете кеш меньшим запросом а потом имеете вдвое большую производительность на удвоенном объёме данных.
Плюс никакого контроля как БД ведут себя под нагрузкой, для примера:
100мс на запрос при 1000 запросов в секунду это не 10мс на запрос при 100 запросах в секунду — на графиках скорости запроса выигрывает Б хотя по факту выигрывает А
Про настройки БД выше уже спросили, криво настроенный мускул это не прямо настроенный мускул, для Neo4j думаю то-же самое

Ну и на мякотку — вы сравнили производительность на разных запросах, вы генерируете два сета случайных чисел вместо одного, а значит одной БД достаётся работы больше другой.
Плюс всегда уместен контроль идентичности результатов, что и А и Б возвращают одинаковые данные.
Я бы дополнительно к выше сказанному попросил бы выложить схему БД в виде дампа.
Вообще лучше все скрипты и данные положить на github.
«вы сравнили производительность на разных запросах, вы генерируете два сета случайных чисел вместо одного» — я использовал псевдослучайные числа. «Random r = new Random(count); » — гарантирует то, что два множества буду равны
Да, посмотрел документацию, тут вы правы
Какой версии использовался mysql? Почему он, а не postgresql? Mysql, сейчас, далеко не показатель эффективной работы реляционной БД.
В постгре, к слову, есть модуль pg_routing, который использует примерно те же алгоритмы для траверсинга графа.
Очень странные результаты, присоединяюсь можно увидеть все коды и все скрипты настройки? Пока все выглядит так что или индексы не созданы/созданы неправильно, либо запросы созданы неверно, вроде того что не использовались join'ы и т.п. вещи в sql.
Запросы были куда уж легкие, индексы и так по PK, если так уж интересно, последний запрос выглядит следущим образом
String sql_query = «SELECT COUNT(DISTINCT social_network.photo.id) » +
«FROM (social_network.photo INNER JOIN social_network.user_photo » +
" ON social_network.photo.id = social_network.user_photo.photo_id) " +
«WHERE social_network.user_photo.user_id IN ( » +
" SELECT social_network.group.user_id\n" +
" FROM social_network.group " +
" WHERE social_network.group.user_id < ?);";
1) Зачем все это? Чем запрос-то

SELECT COUNT(DISTINCT photo.id)
FROM photo, user_photo, group
WHERE photo.id = user_photo.photo_id and
user_photo.user_id = group.user_id and user_photo.user_id < ?;

не понравился? Зачем эти ужасные вложенные запросы по полю без индекса?

2) Я так понимаю что вы индексы на group.user_id и user_photo.photo_id не строили? Тогда о каком сравнении двух баз идет речь, если вы изначально перебили mysql ноги заставляя её заниматься прямым перебором, в то время Neo4j естественно построила индексы (откуда по вашему взялся этот размер)? Реляционная база вовсе не обязана обеспечивать быстрый поиск если вы не построили реляционных индексов по связующим полям.
У вебадминке Neo4j нету ни одного индекса. Врядли, без указаний пользователя, база даных будет их создавать. Индексы и запрос поправлю, сделаю апдейт. P.S. Это не обьясняет результаты остальных експерементов.
Сама по себе графическая база данных уже индекс, так как практически любой индекс это и есть граф (обычно красно-черные деревья), физически не может быть графической базы данных без индексов связей между сущностями.
Описание эксперимента: измерение времени нахождения общего количества фотографий у пользователей, которые «администрируют хотя бы одну группу», в зависимости от диапазона значений идентификаторов пользователя. — в вашем запросе оно подсчитывает фотографии любого пользователя
А вы попробуйте выполнить и свой и мой и найти десять хоть какое-то отличие. Может быть вы и планировали более хитрую логику, но тогда у вас просто не верный запрос:

Смотрим на запрос
… user_photo.user_id IN (
SELECT user_id
FROM group
WHERE user_id < ?)

и понимаем что он выполняется только при user_photo.user_id = group.user_id and user_id <?.. Если не верите давайте представим что group содержит user_id = {1,2,3,5,7,8,9}, user_photo.user_id = {1,2,32,41}, ограничение? = 6, то когда запрос с in выполняется? Правильно при {1,2}, т.е. при user_photo.user_id = group.user_id and user_photo.user_id <?..
Да, и я правильно понимаю что у вас обе базы работали одновременно на одной машине? Мне кажется это не лучшая идея для замера производительности, так как одна база легко может «слопать» себе всю доступную память и время работы винчестера оставив второй жалкие крохи, в результате выиграет самая прожорливая. Оптимальнее делать замеры по очереди на одной базе, остановив другую, в несколько заходов.
К сожелению да, это могло бы быть причной отклонений, но не таких уж больших.
Ну попробуйте запустить последний Oracle и MySql вместе на простенькой персоналке. Oracle слопаем всю память и уйдет в файл подкачки даже толком не начав работу, забив все остальные приложения в системе ногами.
Оно не умеет делать k-shortest paths, например.
Ну я там и читал, и не нашел. Где?
Подзаголовок Shortest Path

SELECT * FROM oq_graph WHERE latch='breadth_first' AND origid=1 AND destid=6;
+----------+--------+--------+--------+------+--------+
| latch | origid | destid | weight | seq | linkid |
+----------+--------+--------+--------+------+--------+
| dijkstras| 1 | 6 | NULL | 0 | 1 |
| dijkstras| 1 | 6 | 1 | 1 | 2 |
| dijkstras| 1 | 6 | 1 | 2 | 6 |
+----------+--------+--------+--------+------+--------+

Note that nodes are uni-directional, so there is no path from node 6 to node 1:

SELECT * FROM oq_graph WHERE latch='dijkstras' AND origid=6 AND destid=1;
Empty set (0.00 sec)
Dijkstra в этом исполнении ищет только один самый дешевый путь, а я говорю про k-shortest paths, т.е. список самых дешевых путей отсортированных по весу. Pg_routing, например, умеет это делать. И Neo4j умеет через Java API.

A* тоже, кстати, не умеет.
www.youtube.com/watch?v=Mw0Vimj39cI
Это доклад про java benchmarking. Крайне советую. Не потому, что это напрямую относится к теме статьи, а потому что, надеюсь, даст понимание, что для того, чтобы сделать замеры производительности, надо очень много знать и делать очень вдумчивые эксперименты.

На вашем бы месте я бы совсем не думал бы о бенчмарках, а сравнивал алгоритмы, подходы, основные задачи, простоту использования и т.п. Где хорошо использовать одно, а где другое без оглядки на производительность.
Спасибо за совет, мне было интересноименно посмотреть как это дело происходит на практике. Этот процес затрагивает очень много другого, одними алгоритмами поиска здесь не обойтись, надо еще как минимум оптимизатора учитывать.
Neo4j — то еще днище, если честно.
У меня сейчас аналогичная задача, только практическая — «отражение» части социального графа ВК на локальную базу, чтобы потом над ним манипулировать.
Оно отожрало все ресурсы системы (стандартный сервер за 20 баксов, ssd, 2 гига, 2 ядра) и медленно стагнирует, сейчас в базе около 0.3кк нод (с минимально нужным количеством данных, у большинства даже текстовых полей нет), они набились где-то за 3 дня. Мои теоретические расчеты говорили мне, что за 3 дня у меня будет все «отражение» в ~20кк нод, в жизни не думал, что уткнусь в производительность записи БД.
Запросы при этом уже оптимизированные, до оптимизаций за три дня было создано где-то 40к нод.

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

Интересно, удалось ли Вам опробовать в деле Titan или PostgreSQL? И какие выводы?

Я на днях тестировал производительность neo4j на графе (200 000 вершин, 3 000 000+ дуг), который сейчас обслуживает PG. Причём не Embedded Java API, а на Cypher. Это было убогое днище, neo4j был буквально на порядок хуже. Я думаю, автор просто напорол чуши с настройкой мускула.
Cypher сам по себе туповат, более того, у pg_routing более продвинутые алгоритмы поиска по графу. Cypher использует просто BFS сортируя результаты впоследствии.

Впрочем, pg_routing очень даже запросто загибается при поиске по большому ветвистому графу.
Sign up to leave a comment.

Articles