Dgraph — распределённая графовая база данных, которая хранит данные в виде узлов и связей и отвечает на запросы на языке DQL. Здесь не нужно писать JOIN: связи — первоклассные сущности, а обход графа выполняется одним запросом. Разберём запуск, схему, мутации, выборки, загрузку данных и эксплуатацию.

Как устроен Dgraph

Кластер состоит из двух ролей. zero хранит метаданные, распределяет предикаты по группам и управляет членством; alpha обслуживает запросы, хранит данные в Badger и реплицирует их через Raft. Предикаты шардируются по группам, поэтому один большой граф можно разнести по машинам.

Узлы адресуются 64-битными UID, а не первичными ключами. Схема описывает предикаты и индексы; типы (type Person) помогают с удалением и раскрытием полей, но не заменяют схему. Ядро — Apache 2.0, корпоративные функции — по отдельной лицензии; перед коммерческим использованием проверьте условия версии.

Быстрый запуск

services:
  zero:
    image: dgraph/dgraph:latest
    command: dgraph zero --my=zero:5080
    ports:
      - "5080:5080"
      - "6080:6080"
    volumes:
      - ./data/zero:/dgraph

  alpha:
    image: dgraph/dgraph:latest
    command: dgraph alpha --my=alpha:7080 --zero=zero:5080 --security "whitelist=0.0.0.0/0"
    ports:
      - "8080:8080"
      - "9080:9080"
    volumes:
      - ./data/alpha:/dgraph

Порты: 8080 — HTTP для запросов и мутаций, 9080 — gRPC, 5080 и 6080 — Zero. Whitelist 0.0.0.0/0 допустим только в локальной лаборатории: в продакшене ограничьте подсеть и включите TLS.

docker compose up -d
docker compose ps
curl -s http://localhost:8080/health?all
docker compose logs --tail=50 alpha

Запрос здоровья возвращает состояние Alpha и Zero. Если Alpha не поднялся, смотрите логи: обычно он не видит Zero или ему не хватает памяти.

Схема и индексы

Схема задаётся через /alter. Индекс нужен для фильтров: без него eq() и ge() не сработают. @reverse добавляет обратное ребро, @unique требует индекс и автоматически включает upsert-проверку.

curl -X POST localhost:8080/alter \
  -H 'Content-Type: application/dql' --data-binary '
name: string @index(term) @lang .
age: int @index(int) .
email: string @index(exact) @unique .
friend: [uid] @reverse .
works_for: uid @reverse .

type Person {
  name
  age
  email
  friend
  works_for
}'

@index(term) подходит для поиска по словам, exact — для точного совпадения, hash — для уникальных идентификаторов. Для полнотекстового поиска с языком используйте @lang, но помните, что язык влияет на токенизацию и должен быть одинаковым при записи и чтении.

Мутации

Данные можно писать в JSON или RDF. JSON удобнее для приложения: blank node _:alice превращается в UID, а вложенные объекты создают связи.

curl -X POST 'localhost:8080/mutate?commitNow=true' \
  -H 'Content-Type: application/json' \
  -d '{
    "set": [
      {"uid": "_:alice", "name": "Alice", "age": 34,
       "email": "alice@example.org"},
      {"uid": "_:bob", "name": "Bob", "age": 41,
       "friend": [{"uid": "_:alice"}],
       "works_for": {"uid": "_:acme"}},
      {"uid": "_:acme", "name": "ACME"}
    ]
  }'

Повторная отправка JSON создаст дубликаты. Для идемпотентной записи используйте upsert: найдите узел по уникальному email и обновите его.

curl -X POST localhost:8080/mutate?commitNow=true \
  -H 'Content-Type: application/dql' --data-binary '
upsert {
  query {
    q(func: eq(email, "alice@example.org")) {
      v as uid
    }
  }
  mutation {
    set {
      uid(v) <age> "35"^^<xs:int> .
    }
  }
}'

Если узел не найден, uid(v) пуст и мутация ничего не сделает. Создание и проверку объединяйте в одном upsert-блоке с директивой @if: это защитит от дубликата при гонке клиентов.

Запросы на DQL

curl -s localhost:8080/query \
  -H 'Content-Type: application/dql' --data-binary '
{
  people(func: has(name), first: 10, orderasc: name)
    @filter(ge(age, 30) AND lt(age, 50)) {
    uid
    name
    age
    friend {
      name
    }
  }
}'

Корневая функция выбирает стартовый набор, фильтр сужает его, а вложенные блоки обходят связи. first и offset дают пагинацию, after — курсорную по UID. Для глубокого обхода используйте @recurse.

{
  network(func: eq(name, "Alice")) @recurse(depth: 3) {
    name
    friend
  }
}

@recurse может вернуть много данных: ограничивайте глубину и не запускайте его на плотном графе без необходимости. Для агрегатов используйте value-переменные или встроенные функции:

{
  stats(func: has(age)) {
    count(uid)
    total: sum(age)
    average: avg(age)
    oldest: max(age)
  }
}

DQL также поддерживает @groupby, @normalize и фасеты — пары «ключ-значение» на ребре. Фасеты не индексируются, поэтому фильтровать по ним в корне нельзя.

GraphQL-эндпоинт

Dgraph генерирует GraphQL API из SDL. Это не DQL: схема и резолверы создаются автоматически, а запросы идут на /graphql.

curl -X POST localhost:8080/admin/schema \
  -H 'Content-Type: application/graphql' --data-binary '
type Person {
  id: ID!
  name: String! @search(by: [term])
  age: Int @search
  worksFor: Company @hasInverse(field: employees)
}

type Company {
  id: ID!
  name: String! @search(by: [term])
  employees: [Person]
}'

После загрузки схемы появляется запрос queryPerson с фильтрами и пагинацией. GraphQL удобен для фронтенда, DQL — для аналитики и сложных обходов. У них разные схемы, поэтому не смешивайте языки в одном слое.

Загрузка данных и эксплуатация

Для разового импорта большого набора используйте bulk loader, для инкрементальной загрузки — live loader. Оба принимают RDF и схему.

dgraph live -f people.rdf -s schema.dql \
  -a localhost:9080 -z localhost:5080
dgraph bulk -f people.rdf.gz -s schema.dql --reduce_shards=1

Bulk loader работает только на пустой базе и создаёт каталог out с данными для копирования. Live loader пишет в работающий кластер и обновляет узлы по UID.

Для резервной копии используйте экспорт через Admin API: он выгружает RDF и схему в каталог export внутри тома Alpha. Проверяйте, что копия действительно восстанавливается.

curl -s localhost:8080/admin/export
docker compose logs -f alpha
du -sh data/alpha

Alpha держит индексы и кэш, а Badger активно пишет value log. При OOM уменьшите кэш, добавьте RAM или разнесите предикаты по группам. Диск должен быть быстрым: случайные чтения чувствительны к задержкам.

Когда Dgraph уместен

  • Социальный граф, рекомендации, связи между сущностями, где важны обходы на 2–4 уровня.
  • Каталог с большим числом связей и частыми запросами «кто с кем связан».
  • Нужен GraphQL API без ручных резолверов.

Для отчётов и небольших данных проще PostgreSQL или SQLite. Dgraph силён в распределённых графах и большой нагрузке.

Итог

Dgraph — это графовая база для связей, а не универсальная замена SQL. Запустите Zero и Alpha, опишите предикаты с индексами, загрузите данные через live loader и проверьте типовые обходы через /query. Дальше следите за памятью и диском, делайте экспорт и не открывайте HTTP-порт в интернет без TLS и ограничения по подсети.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте