Подход к моделированию, который я часто вижу при работе с пользователями 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 label50%ile: 6.0 75%ile: 6.0 99%ile: 402.60999999999825 Using a generic relationship type and then filtering by relationship property50%ile: 21.0 75%ile: 22.0 99%ile: 504.85999999999785 Using a generic relationship type and then filtering by end node label50%ile: 4.0 75%ile: 4.0 99%ile: 145.65999999999931 Using a specific relationship type50%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 Марка Нидхэма в блоге Марка Нидхэма . |