← назад к разделу

Neo4j хранит данные в виде графа: набор объектов и связей между ними. Это не метафора и не способ нарисовать схему — это буквально то, как данные лежат и как по ним ходят запросы. Разберём модель по частям и поймём, почему обход связей здесь стоит дёшево.

Property graph: четыре кирпичика

Модель Neo4j называется property graph («граф со свойствами») и состоит из четырёх элементов:

  • Узел (node) — сущность: человек, товар, счёт, документ. Аналог строки таблицы, но без фиксированной схемы.
  • Ребро (relationship) — связь между двумя узлами: ДРУЖИТ, КУПИЛ, ПЕРЕВЁЛ. У ребра всегда есть тип и направление (от одного узла к другому).
  • Свойства (properties) — пары «ключ-значение» на узлах и на рёбрах: у человека имя и возраст, у ребра «работает в» — должность и с какого года. То, что связь сама несёт данные, — важная особенность: в реляционной базе для этого пришлось бы заводить отдельную таблицу.
  • Метки (labels) — теги на узлах, группирующие их по типу: :Person, :Company. Один узел может иметь несколько меток. По метке Neo4j понимает, где искать, и к ней можно привязать индекс.

Схема при этом не жёсткая: новый тип связи — это просто рёбра с новой меткой, без миграций и перекройки таблиц. Это главная сила модели — лёгкость развития.

Index-free adjacency: почему обход дёшев

Ключевое отличие от реляционной базы — как хранятся связи. В SQL, чтобы пройти от заказа к его позициям, база берёт order_id, идёт в индекс таблицы позиций и ищет там подходящие строки. Это поиск по индексу — быстрый, но не бесплатный, и он повторяется на каждом шаге связи.

В Neo4j связь — это прямая ссылка: у каждого узла в памяти лежат указатели на его рёбра, а у ребра — на узлы-концы. Чтобы пройти от узла к соседям, база не ищет ничего в индексе — она идёт по указателю, как по ссылке в структуре данных. Это называется index-free adjacency («смежность без индексов»).

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

Чем это отличается от реляционной модели

Сравним на одном вопросе: «есть ли путь от счёта А к счёту Б через цепочку переводов неизвестной длины».

  • В SQL это рекурсивный запрос (WITH RECURSIVE) по таблице переводов: на каждом шаге — JOIN по индексу, и чем глубже цепочка, тем дороже. Количество шагов не выражается в структуре запроса напрямую.
  • В Neo4j это один шаблон с переменной длиной пути (-[:ПЕРЕВЁЛ*]->), и база просто идёт по ссылкам от счёта А, пока не упрётся в Б или не кончатся рёбра.

Это не значит, что граф всегда лучше. Для «выбрать заказы за месяц с суммой больше N» реляционная база быстрее и проще — там нет обхода связей, там фильтр по значениям. Neo4j выигрывает там, где вопрос — про связи и их глубину, а не про фильтр по полям. Когда именно стоит переходить на графовую СУБД, а когда хватает рекурсивного SQL, разбирает отдельная статья.

Где это применяется

Модель property graph хорошо ложится на данные, где связи разнотипны и живут своей жизнью: соцсети (люди, компании, события), рекомендации (пользователи и товары), антифрод (счета и переводы), граф знаний (сущности из документов). Общий признак один — связей «многие-ко-многим» больше, чем самих сущностей, и запросы идут «по цепочке».

Где спотыкаются начинающие:

  • Путают узлы и свойства. Если по значению нужно искать связи (например, «все, кто в этом городе») — это узел :City, а не строковое свойство city у каждого человека. Свойство — для данных, которые только читают вместе с узлом; узел — для того, что связывает.
  • Забывают направление ребра. У связи всегда есть направление; в запросе можно обходить в обе стороны, но при моделировании направление задают осмысленно (КУПИЛ идёт от человека к товару, а не наоборот).
  • Ждут, что граф ускорит всё. Простые фильтры и агрегаты граф не ускоряет — там реляционная база быстрее. Граф — про обход, а не про GROUP BY.

Дальше — язык Cypher: как выражать шаблоны связей и обходить граф; затем моделирование и эксплуатация — как проектировать граф и какие нужны индексы.