2013-12-12 8 views
0

Neo4j нуба здесь, на Neo4j 2.0.0 сообществоNeo4j падает на четвёртой степени Cypher запроса

У меня есть график база данных 24000 фильмов и 2700 пользователей, а где-то около 60000 LIKE отношения между пользователем и кино ,

Предположим, что у меня есть определенный фильм (фильм1).

START movie1=node:Movie("MovieId:88cacfca-3def-4b2c-acb2-8e7f4f28be04") 
MATCH (movie1)<-[:LIKES]-(usersLikingMovie1) 
RETURN usersLikingMovie1; 

Я могу быстро и легко найти пользователей, которым понравился фильм с вышеуказанным запросом. Я могу следовать этому пути дальше, чтобы получить пользователей, которым понравились те же фильмы, что и люди, которым понравился фильм1. Я называю это поколение 2 пользователей

START movie1=node:Movie("MovieId:88cacfca-3def-4b2c-acb2-8e7f4f28be04") 
MATCH (movie1)<-[:LIKES]-(usersLikingMovie1)-[:LIKES]->(moviesGen1)<-[:LIKES]-(usersGen2) 
RETURN usersGen2; 

Этот запрос занимает около 3 секунд и возвращает 1896 пользователей.
Теперь я беру этот вопрос один шаг дальше, чтобы получить фильмы понравившихся вышеупомянутых пользователей (поколения 2 видео)

START movie1=node:Movie("MovieId:88cacfca-3def-4b2c-acb2-8e7f4f28be04") 
MATCH (movie1)<-[:LIKES]-(usersLikingMovie1)-[:LIKES]->(moviesGen1)<-[:LIKES]-(usersGen2)-[:LIKES]->(moviesGen2) 
RETURN moviesGen2; 

Этого запрос вызывает Neo4j вращаться в течение нескольких минут при 100% загрузке процессора и использование 4 Гб оперативной памяти , Затем он отправляет исключение «OutOfMemoryError: превышение верхнего предела GC».

Я надеялся, что кто-то сможет мне помочь и объяснить мне вопрос. Неужели Neo4j не предназначен для обработки запроса этой глубины в совершенной манере? Что-то не так с моим запросом Cypher?

Спасибо, что нашли время для чтения.

+0

Любой шанс поделиться своим набором данных? –

+0

Привет, пожалуйста, просмотрите комментарий выше. –

+0

извините, не могу поделиться им. В любом случае, это не база фильмов. – HaterTot

ответ

1

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

Проблема с предложением MATCH, которое вы используете, заключается в том, что иногда Cypher будет находить разные шаблоны, где «usersGen2» совпадает с «usersLikingMovie1», а «movie1» совпадает с «movieGen1» по разным шаблонам. Таким образом, по сути, Cypher находит шаблон каждый раз, когда он существует в Графе, удерживает его в памяти на время запроса и затем возвращает все узлы moviesGen2, которые могут фактически быть одним и тем же узлом n раз.

MATCH (movie1)<-[:LIKES]-(usersLikingMovie1)-[:LIKES]->(moviesGen1)<-[:LIKES]-(usersGen2) 

Если вы явно сказать Cypher, что фильмы и пользователи должны быть разными для каждого шаблона матча он должен решить эту проблему. Попробуй это? Кроме того, параметр DISTINCT позволит вам только захватить каждый узел «movieGen2» один раз.

START movie1=node:Movie("MovieId:88cacfca-3def-4b2c-acb2-8e7f4f28be04") 
MATCH (movie1)<-[:LIKES]-(usersLikingMovie1)-[:LIKES]->(moviesGen1)<-[:LIKES]-(usersGen2)-[:LIKES]->(moviesGen2) 
WHERE movie1 <> moviesGen2 AND usersLikingMovie1 <> usersGen2 
RETURN DISTINCT moviesGen2; 

Кроме того, в 2.0 положение начала не требуется. Таким образом, вы можете вообще исключить предложение START (однако - только если вы НЕ используете устаревший индекс и используете метки) ...

Надеюсь, что это сработает ... Пожалуйста, исправьте мой ответ, если есть синтаксические ошибки. ..

+1

Вы не можете смешивать устаревшие индексы в cypher без предложения start. :/ –

+1

Я хотел поблагодарить вас за то, что вы впервые ответили и за то, что смогли сразу определить серьезную ошибку в моем мышлении. После тестирования различных аспектов ваших предложений я обнаружил, что основной проблемой, которую я вызывал, было то, что я пытался вернуть целые объекты Movie, которые также вернулись со своими отношениями, и именно это привело к взрыву JVM. После того как я изменил ваши запросы, чтобы вернуть только MovieIds, запрос занял одну минуту, используя только 30% процессор в этом процессе. – HaterTot

+0

Спасибо Wes! Хороший звонок! – Huston

1

Это довольно интенсивный запрос, и чем глубже вы идете, тем ближе вы, вероятно, попадаете в набор всех пользователей, которые когда-либо оценивали фильм, поскольку вы по существу просто расширяетесь через график в виде дерева начиная с вашего фильма. @ Huston's WHERE и DISTINCT статьи помогут обрезать ветки, которые вы уже видели, но вы все еще просто расширяетесь через дерево.

The branching factor из дерева может быть оценен с двумя значениями:

  • и, среднее количество пользователей, которые понравились кино (входящее в: Movie)
  • м, среднее количество фильмов, каждому пользователю нравится (исходящий от: Пользователь)

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

gen(1) => u 
gen(2) => u * (m * u) 

Для каждого поколения вы будете лавировать на другой m*u, так что ваша треть поколение:

gen(3) => u * (m * u) * (m * u) 

Или более обобщенно:

gen(n) => u^n * m^(n-1) 

Вы могли бы оценить ваше ветвление факторов, вычисляя среднее число ваших увлечений/пользователей и любят/кино, бушель t, что, вероятно, очень неточно, так как он дает вам 22,2 понравившиеся/пользовательские и 2.5 понравившиеся/фильмы. Эти цифры не подходят для любого фильма, достойного рейтинга. Лучшим подходом было бы взять среднее число оценок или посмотреть на гистограмму и использовать пики в качестве ваших факторов ветвления.

Чтобы поместить это в перспективу, average Netflix user rated 200 movies. Учебный комплект Netflix Prize имел 17 770 фильмов, 480 189 пользователей и 100 480,507 оценок. Это 209 оценок/пользователей и 5654 оценок/фильмов.

Для простоты (и предполагается, что ваш набор данных гораздо меньше), давайте использовать:

m = 20 movie ratings/user 
u = 100 users have rated/movie 

Ваш запрос в GEN-3 (без distincts) вернет:

gen(3) = 100^3 * 20^2 
     = 400,000,000 

400 миллионы узлов (пользователей)

Поскольку у вас только 2700 пользователей, я думаю, что можно с уверенностью сказать, что ваш запрос, вероятно, возвращает каждого пользователя в вашем наборе данных (точнее, 148 тысяч-их копий каждого пользователя).

Узлы вашего видео в ASCII - (n:Movie {movieid:"88cacfca-3def-4b2c-acb2-8e7f4f28be04"}) имеют минимум 58 байтов. Если пользователи примерно то же самое, скажем, каждый узел 60 байт, ваше требование для хранения этого результирующего множества:

400,000,000 nodes * 60 bytes 
= 24,000,000,000 bytes 
= 23,437,500 kb 
= 22,888 Mb 
= 22.35 Gb 

Так по моим скромным подсчетам, ваш запрос требует 22 Гбайта памяти. Это кажется вполне разумным, что у Neo4j не хватит памяти.

Я предполагаю, что вы пытаетесь найти сходство в шаблонах пользователей, но запрос, который вы используете, возвращает всех пользователей в вашем наборе данных, дублированных кучей раз. Возможно, вы хотите задать вопросы своим данным больше:

  • что пользователи оценивают фильмы больше всего меня?
  • какие пользователи оценили большинство тех же фильмов, что и я, рейтинг
  • Какие фильмы имеют пользователи, которые оценили похожие фильмы для меня, смотрели, что я еще не смотрел?

Приветствия, см

+0

недостаток с подсчетом памяти заключается в том, что не нужно сохранять весь результат в памяти, когда он просто возвращает одно значение. matcher возвращает итератор к набору результатов сопоставления - здесь не ленится. –

+0

Но количество путей, которые нужно просмотреть, по-прежнему действует. И cypher в настоящее время использует поиск в первом поиске, в котором хранится довольно много состояний в памяти (обходные ветви до сих пор) –

1

Чтобы свести к минимуму взрыв, который @ cod3monk3y говорит о, я бы ограничить количество промежуточных результатов.

START movie1=node:Movie("MovieId:88cacfca-3def-4b2c-acb2-8e7f4f28be04") 
MATCH (movie1)<-[:LIKES]-(usersLikingMovie1)-[:LIKES]->(moviesGen1) 
WITH distinct moviesGen1 
MATCH (moviesGen1)<-[:LIKES]-(usersGen2)-[:LIKES]->(moviesGen2) 
RETURN moviesGen2; 

или даже как это

START movie1=node:Movie("MovieId:88cacfca-3def-4b2c-acb2-8e7f4f28be04") 
MATCH (movie1)<-[:LIKES]-(usersLikingMovie1)-[:LIKES]->(moviesGen1) 
WITH distinct moviesGen1 
MATCH (moviesGen1)<-[:LIKES]-(usersGen2) 
WITH distinct usersGen2 
MATCH (usersGen2)-[:LIKES]->(moviesGen2) 
RETURN distinct moviesGen2; 

, если вы хотите, вы можете использовать «старт профиля ...» в Neo4j оболочке, чтобы увидеть, сколько хитов/DB-строк создается между , начиная с вашего запроса, а затем этих двух.

+0

Спасибо за идею, я дал вашему второму предложению попробовать, и так или иначе это закончилось тем, что запрос занял 10 раз. Возможно, это было потому, что neo4j выполнил полное разрешение узла в каждом операторе WITH? – HaterTot

+0

Вы также испытали другие предложения? Особенно «профиль один»? –

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