Статьи

Neo4j: общие / неопределенные имена отношений

Подход к моделированию, который я часто вижу при работе с пользователями Neo4j, заключается в создании очень общих отношений (например, HAS, CONTAINS, IS) и фильтрации по свойству отношения или по свойству / метке на конечном узле.

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

Однако я никогда не проверял различия в производительности между подходами, поэтому решил попробовать.

Я создал 4 разные базы данных, в которых был один узел с 60 000 исходящих отношений — 10 000, которые мы хотели получить, и 50 000, которые не имели отношения к делу.

Я смоделировал «отношения» 4 разными способами …

  • Использование определенного типа отношения
    (узла) — [: HAS_ADDRESS] -> (адрес)
  • Использование общего типа отношений, а затем фильтрация по метке конечного узла
    (узел) — [: HAS] -> (адрес: адрес)
  • Использование общего типа отношения, а затем фильтрация по свойству отношения
    (узел) — [: HAS {тип: «адрес»}] -> (адрес)
  • Использование общего типа отношений, а затем фильтрация по свойству конечного узла
    (узел) — [: HAS] -> (адрес {тип: «адрес»})

… а затем измерили, сколько времени понадобилось, чтобы получить отношения «имеет адрес».

Код на GitHub , если вы хотите посмотреть.

Хотя он явно не такой точный, как микро-тест JMH, я думаю, что он достаточно хорош, чтобы почувствовать разницу между подходами.

Я провел запрос к каждой базе данных 100 раз, а затем взял 50-й, 75-й и 99-й процентили (время указано в мс):

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

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

Использование определенного типа отношений

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. Мы получаем доступ к базе данных каждый раз, когда загружаем узел, отношение или свойство.

В отличие от других подходов нужно загружать гораздо больше данных только для того, чтобы затем отфильтровать их:

Использование общего типа отношений, а затем фильтрация по метке конечного узла

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

Использование общего типа отношения, а затем фильтрация по свойству отношения

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

Использование общего типа отношений, а затем фильтрация по свойству конечного узла

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!