Когда вы уже поняли, что ваши данные — это граф (связей «многие-ко-многим» больше, чем самих сущностей, и запросы идут «по цепочке»), встаёт следующий вопрос: где именно графовая СУБД оправдывает отдельную базу в эксплуатации. Neo4j — самая известная из них: модель property graph (у вершин и рёбер есть метки и свойства), запросы на языке Cypher. Разберём типовые области, где её берут, и отдельно — свежую, из-за которой про графы снова заговорили: GraphRAG для языковых моделей.
Ни одна строка таблицы не выглядит подозрительной — мошенничество видно только в форме связей. Обычный JOIN двух таблиц показал бы один шаг, а тут длина цепочки заранее неизвестна: обход идёт по рёбрам, пока не вернётся в исходный счёт. Замкнувшееся кольцо и есть находка. Такой обход переменной глубины — та самая повседневная операция, ради которой в областях ниже и заводят отдельную графовую СУБД.
Что Neo4j делает хорошо
Одна идея объясняет все применения ниже: в графовой СУБД ребро — это прямая ссылка от вершины к вершине, а не строка в таблице связей, которую надо искать по индексу. Поэтому обход «на N шагов вглубь» стоит примерно столько, сколько реально пройдено рёбер, и почти не зависит от общего размера базы. Там, где в SQL был бы рекурсивный запрос из нескольких табличных выражений, в Cypher получается короткий шаблон связей. Neo4j выстреливает не когда данных много, а когда много запросов-обходов и они — ядро продукта.
Рекомендации
Классика: «с этим товаром часто покупают», «людям, похожим на вас, понравилось». Данные здесь естественно графовые — пользователи, товары, покупки, оценки соединены рёбрами. Рекомендация — это обход на два-три шага: от пользователя к его покупкам, от них к другим покупателям, от тех — к товарам, которых у исходного пользователя ещё нет. В Neo4j такой обход выражается коротким шаблоном, и для одного пользователя его реально посчитать в момент запроса. Оговорка: так работает обход на пару шагов от конкретной вершины. Тяжёлые вещи вроде похожести всех пользователей со всеми всё равно считают заранее, отдельной библиотекой алгоритмов — она называется Neo4j Graph Data Science, — и раскладывают результат по рёбрам. Про цену стоит узнать сразу: бесплатная редакция библиотеки есть, но она ограничена (например, числом ядер, которые может занять расчёт), а снятие ограничений платное.
Антифрод и расследования
Мошенничество почти всегда видно не в одной записи, а в связях: один и тот же телефон у десятка «независимых» аккаунтов, кольцо переводов, возвращающее деньги отправителю через цепочку счетов, общий адрес доставки у заявок, которые не должны пересекаться. Такие вопросы — «есть ли путь от счёта А к счёту Б», «сколько сущностей завязано на этот идентификатор» — это обход графа переменной глубины. Банки и платёжные системы держат под это графовую базу именно потому, что подозрительную цепочку надо находить в момент операции, а не в отчёте наутро.
Социальные графы
«Друзья друзей», «общие связи», «кратчайшая цепочка знакомств между двумя людьми», лента на основе того, что читают близкие по графу. Это буквально та задача, ради которой понятие «граф» и придумали. Когда социальные связи — не второстепенная деталь, а суть продукта, хранить их рёбрами и обходить графовым языком удобнее, чем городить самосоединения таблицы связей.
Граф знаний
Граф знаний (knowledge graph) — единая связная модель предметной области: сущности (люди, компании, товары, документы, понятия) и типизированные связи между ними, собранные из разных источников в одну сеть. На него опираются, когда надо отвечать на вопросы, соединяющие разрозненные факты: «какие продукты зависят от этой библиотеки и кто их сопровождает», «через какие компании связаны эти два человека». Neo4j часто выступает хранилищем такого графа — новую связь добавляют просто новым ребром с новой меткой, без переделки схемы.
Сети и инфраструктура
Топология сети, зависимости микросервисов, состав изделия, маршруты доставки — всё это графы, и типичный вопрос к ним про влияние: «если упадёт этот узел, что перестанет работать», «какие сервисы зависят от этой базы напрямую и по цепочке». Обход по зависимостям — родная для графовой СУБД операция, поэтому её берут для карт инфраструктуры и анализа последствий сбоя.
GraphRAG: граф знаний как память для языковой модели
Самая обсуждаемая сейчас область. Чтобы языковая модель отвечала по вашим документам, а не выдумывала, применяют подход RAG (retrieval-augmented generation): документы режут на куски, для вопроса находят куски, похожие по смыслу (векторный поиск), и подают их модели как контекст. Слабое место — факты про один объект разбросаны по разным кускам, а связи между объектами теряются: модель видит похожие абзацы, но не видит, как они соотносятся. На «многошаговых» вопросах («как A влияет на C через B») отсюда пробелы и выдумки.
GraphRAG добавляет к этому граф знаний. Из документов заранее вытаскивают сущности и связи (обычно той же языковой моделью) и складывают в граф. При запросе система не только ищет похожие куски, но и ходит по графу вокруг найденных сущностей: собирает соседей, цепочки связей, а иногда заранее посчитанные сводки по кластерам графа. Модель получает не мешок абзацев, а связную карту предметной области — точнее отвечает на вопросы про отношения и реже галлюцинирует.
Почему тут именно Neo4j. Граф знаний надо где-то хранить и быстро обходить — это его прямая работа. Вдобавок в Neo4j держат векторные индексы прямо на узлах графа, так что поиск похожего и обход по связям идут в одной базе: находим релевантные сущности векторным поиском и тут же расширяем ответ их графовым окружением. Эта комбинация «вектора + связи в одном месте» и сделала графовые СУБД заметными в теме языковых моделей.
Две оговорки, чтобы не строить планы на пустом месте. Векторные индексы появились только в Neo4j 5 (в версиях 5.11 и 5.13), то есть на более старой установке их просто нет. И по самому векторному поиску Neo4j скромнее специализированных хранилищ — и по размерности вектора, и по числу векторов, которые он тянет без потери скорости. Если в проекте миллионы кусков текста, вектора обычно всё-таки держат отдельно, а граф оставляют для связей.
Где это применяется
Общее правило: Neo4j оправдан там, где обход связей — повседневная и массовая операция, а не разовая. Рекомендации, антифрод, социальные фичи, граф знаний, карты зависимостей, GraphRAG — во всех этих случаях вопрос «как связано» задаётся постоянно и на переменную глубину, и ради него не жалко отдельной базы.
Где спотыкаются начинающие:
- Берут графовую СУБД под одну иерархию. Дерево категорий или оргструктура — это рекурсивный SQL в той базе, что уже есть, а не повод заводить вторую систему.
- Забывают цену второй базы. Репликация, бэкапы, мониторинг, ещё одна модель в голове у команды — та же плата, что за любое полиглотное хранилище. Она окупается ядром продукта, а не одной фичей.
- Считают, что всё это доступно бесплатно. У Neo4j бесплатная редакция одна база, без кластера и без горячего бэкапа живой базы: копию снимают с остановленной, а остановить единственную базу — значит остановить сервис. Кластер, горячий бэкап и часть ограничений схемы — платная редакция. Подробнее — в статье про моделирование и эксплуатацию.
- Ждут от GraphRAG магии. Качество ответов упирается в качество графа: если сущности и связи вытащены из документов небрежно, обход соберёт мусор. Граф знаний — это данные, за которыми надо следить, а не бесплатный побочный эффект.
- Дублируют весь граф без нужды. Если графовые запросы — 5 % нагрузки, честнее оставить данные в PostgreSQL (вершины и рёбра двумя таблицами), а не синхронизировать две базы.
Глубже: с кем сравнивать Neo4jрасширенное
Neo4j — самая известная графовая база, но не единственная, и выбор стоит делать осознанно.
Memgraph — совместим с Cypher, держит граф в памяти и потому быстрее на обходах, интересен для потоковых сценариев (антифрод в реальном времени). Цена — память как обязательное условие, а не как кэш.
ArangoDB — многомодельная: документы, граф и поиск в одной базе. Берут, когда граф нужен рядом с документной моделью и не хочется второй системы.
Amazon Neptune — управляемая графовая база в облаке Amazon, поддерживает openCypher и Gremlin. Эксплуатации нет, зато есть привязка к провайдеру.
TigerGraph — рассчитан на очень большие графы и распределённые обходы; порог входа выше, ниша — аналитика на графах в десятки миллиардов рёбер.
Apache AGE поверх PostgreSQL — не отдельная база, а расширение: графовые запросы на openCypher внутри той базы, которая уже есть. Производительность специализированной базы оно не даёт, зато не добавляет второй системы в эксплуатацию — и это правильный первый шаг, когда графовых запросов немного.
Порядок выбора обычно такой: сначала проверить, не хватает ли рекурсивного SQL, потом расширение поверх основной базы, и только потом отдельная графовая база — а из них Neo4j как наиболее зрелая и обученная рынком.
Глубже: порядок величин: где начинается больрасширенное
Цифры в графовых базах привязаны не к числу узлов, а к тому, сколько связей обходит запрос.
Обход в памяти идёт со скоростью порядка миллионов переходов по связям в секунду на ядро. Значит, запрос на два шага от узла со сотней связей на каждом уровне (десять тысяч переходов) — это единицы миллисекунд, и таких запросов один сервер выдерживает тысячи в секунду. Тот же запрос через узел с сотней тысяч связей — это уже сотни миллисекунд, и он же под нагрузкой кладёт сервис.
Отсюда практические границы. Граф, который целиком влезает в память машины (узлы, связи и индексы), ведёт себя предсказуемо; ориентир по памяти — сумма файлов хранилища плюс запас. Граф в разы больше памяти требует, чтобы горячая часть умещалась в кэш страниц, иначе каждый переход становится чтением с диска — на два порядка дороже. Запись масштабируется одной машиной: в типичной установке пишет один узел, реплики обслуживают чтение.
Что из этого следует для выбора: графовая база оправдана не «когда данных много», а когда обходы частые и глубокие, а горячая часть графа влезает в память. Если граф больше памяти и растёт линейно, разговор переходит к специализированным решениям или к пересмотру модели.
Глубже: как граф попадает в базурасширенное
Граф почти никогда не является первичным хранилищем: данные рождаются в основной базе, а в граф попадают производными. Схема устойчивой установки такая.
Источник правды — основная база. Граф можно пересобрать с нуля в любой момент; при расхождении его пересобирают, а не выясняют, чья версия вернее.
Первичная загрузка — пакетная выгрузка и массовая заливка: neo4j-admin database import для пустой базы, LOAD CSV с порционными транзакциями для дозагрузки. Перед загрузкой создают ограничения уникальности на ключи, по которым идёт MERGE.
Поток изменений — либо события приложения (достаточно, когда в базу пишет одна служба), либо захват изменений из журнала базы (когда служб несколько). Обработка обязана быть идемпотентной: MERGE вместо CREATE, тогда повтор события не плодит дубли.
Сверка и задержка — счётчики узлов и связей против источника плюс контроль отставания потока. Если граф отстал на часы, функция, которая на него опирается, должна честно деградировать, а не показывать устаревшие связи как свежие.
Путь данных в граф сверху вниз: источник правды остаётся в основной базе, поэтому граф пересобирают с нуля, а не чинят по месту.
Глубже: GraphRAG: что там на самом деле делаетсярасширенное
Граф знаний как память для языковой модели звучит просто, а состоит из четырёх работ, каждая со своей ценой.
Извлечение сущностей и связей. Из текста документов надо получить узлы и рёбра, и делает это либо сама языковая модель по схеме (просишь вернуть список сущностей и связей в заданном формате), либо отдельная модель распознавания именованных сущностей, либо правила для простых случаев. Первое дороже и качественнее, третье дешевле и ломается на живом языке. Ключевой шаг, о котором забывают, — сведение сущностей: «ООО Ромашка», «Ромашка» и «romashka ltd» должны стать одним узлом, иначе граф распадается на дубли.
Обновление при изменении документов. Документ переписали — старые узлы и связи, извлечённые из него, надо убрать, а новые добавить. Поэтому у каждого ребра и узла хранят, из какого документа и какой его версии он получен; тогда обновление сводится к «удалить всё от версии N, добавить от N+1». Без этого граф накапливает противоречия.
Оценка качества. Без неё вся затея превращается в веру. Минимальный набор: размеченный набор вопросов с правильными ответами, метрика «нашёл ли обход нужный фрагмент» и сравнение с обычным поиском по векторам. Если графовый поиск не выигрывает у векторного на ваших вопросах, граф не нужен.
Цена перестройки. Полное извлечение по корпусу — это вызов модели на каждый фрагмент: тысяча документов по десять фрагментов — десять тысяч вызовов, и это измеримые деньги и часы. Поэтому перестройку с нуля планируют как отдельную операцию, а обычный режим — инкрементальный, по изменившимся документам.
Вывод для выбора: GraphRAG оправдан, когда вопросы пользователей действительно требуют связей между документами («как связаны эти две компании», «кто участвовал в цепочке поставок»), а не просто похожего текста. Для «найди похожее» достаточно векторного поиска, который дешевле в разы.
Коротко
- Neo4j оправдан, когда обход связей — повседневная и массовая операция ядра продукта: рекомендации, антифрод, граф знаний, карты зависимостей.
- Сначала проверяют, не хватает ли рекурсивного SQL, потом графовое расширение поверх основной базы (Apache AGE), и только потом отдельную базу; рядом с Neo4j стоят Memgraph, ArangoDB, Neptune и TigerGraph.
- Порядок величин: миллионы переходов в секунду, пока горячая часть графа влезает в память; узел с сотней тысяч связей делает любой обход через него дорогим.
- Граф — производные данные: источник правды остаётся в основной базе, первичная заливка пакетная, дальше идемпотентный поток изменений, сверка по счётчикам и контроль отставания.
- GraphRAG — это извлечение сущностей со сведением дублей, обновление по версиям документов, оценка качества против векторного поиска и посчитанная цена перестройки.
Что почитать дальше
- Property graph и index-free adjacency — как устроена графовая база.
- Cypher на примерах — язык запросов: шаблоны, пути,
WITH. - Моделирование и эксплуатация Neo4j — индексы, супер-узлы, память, загрузка.
- Иерархии в SQL против графовой базы — когда обойтись рекурсивным запросом.