Подход к моделированию, который я часто вижу при работе с пользователями Neo4j, заключается в создании очень общих отношений (например, HAS, CONTAINS, IS) и фильтрации по свойству отношения или по свойству / метке на конечном узле.
Интуитивно понятно, что это не позволяет наилучшим образом использовать графовую модель, поскольку это означает, что вам нужно оценить множество связей и узлов, которые вас не интересуют.
Однако я никогда не проверял различия в производительности между подходами, поэтому решил попробовать.
Я создал 4 разные базы данных, в которых был один узел с 60 000 исходящих отношений — 10 000, которые мы хотели получить, и 50 000, которые не имели отношения к делу.
Я смоделировал «отношения» 4 разными способами …
- Использование определенного типа отношений
(Узел) — [: HAS_ADDRESS] -> (адрес) - Использование общего типа отношений, а затем фильтрация по метке конечного узла
(Узел) — [: HAS] -> (адрес: Адрес) - Использование общего типа отношения, а затем фильтрация по свойству отношения
(узел) — [: HAS {тип: «адрес»}] -> (адрес) - Использование общего типа отношений, а затем фильтрация по свойству конечного узла
(узел) — [: HAS] -> (адрес {тип: «адрес»})
… а затем измерили, сколько времени понадобилось, чтобы получить отношения «имеет адрес».
- Код на github, если вы хотите посмотреть.
Хотя он явно не такой точный, как микро-тест JMH, я думаю, что он достаточно хорош, чтобы почувствовать разницу между подходами.
Я провел запрос к каждой базе данных 100 раз, а затем взял 50-й, 75-й и 99-й процентили (время указано в мс):
01
02
03
04
05
06
07
08
09
10
11
|
Using a generic relationship type and then filtering by end node label 50 %ile: 6.0 75 %ile: 6.0 99 %ile: 402.60999999999825 Using a generic relationship type and then filtering by relationship property 50 %ile: 21.0 75 %ile: 22.0 99 %ile: 504.85999999999785 Using a generic relationship type and then filtering by end node label 50 %ile: 4.0 75 %ile: 4.0 99 %ile: 145.65999999999931 Using a specific relationship type 50 %ile: 0.0 75 %ile: 1.0 99 %ile: 25.749999999999872 |
Мы можем подробнее изучить причину различий во времени для каждого из подходов, профилировав эквивалентный запрос шифра. Начнем с того, которое использует конкретное имя отношения:
Использование определенного типа отношений
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
neo4j-sh (?)$ profile match (n) where id(n) = 0 match (n)-[:HAS_ADDRESS]->() return count(n); +----------+ | count(n) | +----------+ | 10000 | +----------+ 1 row ColumnFilter | +EagerAggregation | +SimplePatternMatcher | +NodeByIdOrEmpty +----------------------+-------+--------+-----------------------------+-----------------------+ | Operator | Rows | DbHits | Identifiers | Other | +----------------------+-------+--------+-----------------------------+-----------------------+ | ColumnFilter | 1 | 0 | | keep columns count(n) | | EagerAggregation | 1 | 0 | | | | SimplePatternMatcher | 10000 | 10000 | n, UNNAMED53, UNNAMED35 | | | NodeByIdOrEmpty | 1 | 1 | n, n | { AUTOINT0} | +----------------------+-------+--------+-----------------------------+-----------------------+ Total database accesses: 10001 |
Здесь мы видим, что было 10 002 доступа к базе данных, чтобы подсчитать наши 10 000 отношений HAS_ADDRESS. Мы получаем доступ к базе данных каждый раз, когда загружаем узел, отношение или свойство.
В отличие от других подходов нужно загружать гораздо больше данных только для того, чтобы затем отфильтровать их:
Использование общего типа отношений, а затем фильтрация по метке конечного узла
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
neo4j-sh (?)$ profile match (n) where id(n) = 0 match (n)-[:HAS]->(:Address) return count(n); +----------+ | count(n) | +----------+ | 10000 | +----------+ 1 row ColumnFilter | +EagerAggregation | +Filter | +SimplePatternMatcher | +NodeByIdOrEmpty +----------------------+-------+--------+-----------------------------+----------------------------------+ | Operator | Rows | DbHits | Identifiers | Other | +----------------------+-------+--------+-----------------------------+----------------------------------+ | ColumnFilter | 1 | 0 | | keep columns count(n) | | EagerAggregation | 1 | 0 | | | | Filter | 10000 | 10000 | | hasLabel( UNNAMED45:Address( 0 )) | | SimplePatternMatcher | 10000 | 60000 | n, UNNAMED45, UNNAMED35 | | | NodeByIdOrEmpty | 1 | 1 | n, n | { AUTOINT0} | +----------------------+-------+--------+-----------------------------+----------------------------------+ Total database accesses: 70001 |
Использование общего типа отношения, а затем фильтрация по свойству отношения
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
neo4j-sh (?)$ profile match (n) where id(n) = 0 match (n)-[:HAS {type: "address" }]->() return count(n); +----------+ | count(n) | +----------+ | 10000 | +----------+ 1 row ColumnFilter | +EagerAggregation | +Filter | +SimplePatternMatcher | +NodeByIdOrEmpty +----------------------+-------+--------+-----------------------------+--------------------------------------------------+ | Operator | Rows | DbHits | Identifiers | Other | +----------------------+-------+--------+-----------------------------+--------------------------------------------------+ | ColumnFilter | 1 | 0 | | keep columns count(n) | | EagerAggregation | 1 | 0 | | | | Filter | 10000 | 20000 | | Property( UNNAMED35,type( 0 )) == { AUTOSTRING1} | | SimplePatternMatcher | 10000 | 120000 | n, UNNAMED63, UNNAMED35 | | | NodeByIdOrEmpty | 1 | 1 | n, n | { AUTOINT0} | +----------------------+-------+--------+-----------------------------+--------------------------------------------------+ Total database accesses: 140001 |
Использование общего типа отношений, а затем фильтрация по свойству конечного узла
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
neo4j-sh (?)$ profile match (n) where id(n) = 0 match (n)-[:HAS]->({type: "address" }) return count(n); +----------+ | count(n) | +----------+ | 10000 | +----------+ 1 row ColumnFilter | +EagerAggregation | +Filter | +SimplePatternMatcher | +NodeByIdOrEmpty +----------------------+-------+--------+-----------------------------+--------------------------------------------------+ | Operator | Rows | DbHits | Identifiers | Other | +----------------------+-------+--------+-----------------------------+--------------------------------------------------+ | ColumnFilter | 1 | 0 | | keep columns count(n) | | EagerAggregation | 1 | 0 | | | | Filter | 10000 | 20000 | | Property( UNNAMED45,type( 0 )) == { AUTOSTRING1} | | SimplePatternMatcher | 10000 | 120000 | n, UNNAMED45, UNNAMED35 | | | NodeByIdOrEmpty | 1 | 1 | n, n | { AUTOINT0} | +----------------------+-------+--------+-----------------------------+--------------------------------------------------+ Total database accesses: 140001 |
Итак, в заключение … конкретные отношения #ftw!
Ссылка: | Neo4j: общие / неопределенные имена отношений от нашего партнера JCG Марка Нидхэма в блоге Марка Нидхэма . |