Масштабирование, кластеры и применения

Обзор того, как граф растёт под нагрузкой и где он живёт в реальных системах.

Кластер Neo4j (Enterprise) — несколько серверов: лидер принимает записи, реплики копируют данные и обслуживают чтение, обеспечивая отказоустойчивость и масштаб чтения.

Рано или поздно у любой базы спрашивают: «а выдержит ли она нагрузку и что будет, если сервер упадёт?» У реляционных и key-value баз есть привычные ответы — шардирование, реплики, кластеры. У графа эти ответы звучат иначе, и важно понять почему: граф масштабируется не так, как привыкли инженеры из мира таблиц. Этот урок объясняет, какими путями Neo4j растёт под нагрузкой, как устроена отказоустойчивость через кластер и где граф вообще применяют в реальных системах — чтобы вы понимали и инженерную сторону, и продуктовую.

Вертикаль и горизонталь

Граф плохо «режется» на куски: связь может вести куда угодно, поэтому наивное шардирование ломает обходы (ребро между шардами — дорогое). Поэтому первый путь масштабирования Neo4j — вертикальный: больше RAM, чтобы горячий граф целиком лёг в page cache, и быстрые диски. Здоровый граф, помещающийся в память, отвечает очень быстро.

Почему вертикаль так эффективна именно для графа? Обход — это череда переходов по указателям. Если данные узлов и связей уже в оперативной памяти (в page cache), каждый переход стоит наносекунды. Если же часть графа на диске, переход превращается в случайное чтение с диска — на порядки медленнее, а у обхода таких переходов могут быть тысячи. Поэтому золотое правило эксплуатации Neo4j: дайте серверу достаточно RAM, чтобы «горячая» часть графа целиком помещалась в кэш. Очень часто проблему «медленных запросов» решает не переписывание Cypher, а просто добавление памяти, после которого граф перестаёт ходить на диск.

Кластер: лидер и реплики

Горизонтально масштабируют чтение и отказоустойчивость через кластер:

            записи
  Приложение ──────────────> ( Лидер )
      │                          │ репликация
      │ чтения                   ▼
      ├───────────────> ( Реплика 1 )
      └───────────────> ( Реплика 2 )

Лидер принимает все записи и реплицирует их. Реплики обслуживают запросы на чтение, разгружая лидера. Драйвер сам маршрутизирует: запись — на лидера, чтение — на ближайшую реплику. Если лидер падает, кластер выбирает нового (consensus), и приложение через ретраи это переживает.

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

Шардирование через Fabric

Когда граф не влезает в одну машину, в Enterprise есть Fabric: граф разносят по нескольким базам, а запросы федерируют. Это сложно и применяется, когда вертикали и реплик уже не хватает; для большинства систем до этого не доходит.

Почему шардирование графа — крайняя мера, а не первый шаг? Вспомните, что обход — это переход по указателям между соседями. Пока соседи на одной машине, переход дёшев. Но если граф разрезать на части, неизбежно появятся рёбра, чьи концы оказались на разных машинах, — и переход по такому ребру превращается в сетевой вызов между серверами, в сотни раз дороже. У соцграфа или антифрод-графа связи идут «куда угодно», поэтому таких межшардовых рёбер будет много, и обходы резко замедлятся. Fabric помогает, когда граф естественно делится на слабо связанные домены (например, по регионам), но для типичного плотного графа честнее сначала выжать вертикаль и реплики.

Где граф применяют

ОбластьЧто моделируют графом
Соцсетидружба, подписки, лента, рекомендации знакомых
Антифродкольца аккаунтов, общие устройства/карты/адреса
Графы знанийпонятия и факты, поиск, ассистенты
Рекомендации«купили вместе», похожие пользователи/товары
IT-инфраструктуразависимости сервисов, impact-анализ сбоев
Логистика/сетимаршруты, узкие места, кратчайшие пути

Как работает под капотом

Кластер использует протокол консенсуса (Raft) для согласования: запись считается зафиксированной, когда её приняло большинство участников. Это даёт устойчивость к падению меньшинства узлов. Реплики применяют поток транзакций от лидера, отставая на доли секунды (это надо учитывать: чтение с реплики может быть чуть «свежее commit минус лаг»). Маршрутизацию реализует драйвер на основе таблицы ролей кластера.

Частые ошибки

  • Ждать линейного горизонтального масштаба, как у key-value. Граф со связями так не шардируется; основной путь — вертикаль + реплики.
  • Слать записи на реплику. Пишет только лидер; используйте транзакционные функции, и драйвер сам маршрутизирует.
  • Не учитывать реплик-лаг. Только что записанное может на доли секунды не быть видно на реплике.
  • Сразу хвататься за Fabric. Шардирование — крайняя мера; сначала RAM и реплики.

Итоги

  • Граф масштабируют прежде всего вертикально (RAM под весь горячий граф) — связи плохо шардируются.
  • Кластер «лидер + реплики» даёт отказоустойчивость и масштаб чтения; пишет лидер.
  • Драйвер маршрутизирует чтение/запись; кластер согласуется консенсусом (Raft).
  • Применения: соцсети, антифрод, графы знаний, рекомендации, инфраструктура, логистика.
Проверьте себя
1. Почему граф плохо поддаётся наивному горизонтальному шардированию?
AСлишком мало данных
BСвязь может вести в любой шард, и межшардовые рёбра делают обходы дорогими
CГрафы не масштабируются вообще
DИз-за лицензии
2. Кто в кластере Neo4j принимает записи?
AЛюбая реплика
BТолько лидер; реплики обслуживают чтение
CДрайвер
DВсе узлы одновременно
3. Что значит реплик-лаг для приложения?
AРеплики хранят меньше данных
BТолько что записанное может на доли секунды не быть видно при чтении с реплики
CРеплики ломают ACID
DЛаг отключает кластер