← Назад к вопросам

В чем разница между реляционной и документоориентированной БД?

2.2 Middle🔥 211 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница Между Реляционной и Документоориентированной БД

Это один из самых важных вопросов в современной backend разработке. Выбор между реляционной (SQL) и документоориентированной (NoSQL) БД влияет на всю архитектуру приложения.

Реляционная БД (SQL)

PostgreSQL, MySQL, Oracle, SQL Server

Структура: Таблицы с Fixed Schema

CREATE TABLE users (
  id UUID PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  email VARCHAR(100) UNIQUE NOT NULL,
  age INT CHECK (age >= 0),
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE posts (
  id UUID PRIMARY KEY,
  user_id UUID NOT NULL REFERENCES users(id),
  title VARCHAR(255) NOT NULL,
  content TEXT,
  created_at TIMESTAMP DEFAULT NOW()
);

Характеристики:

  • Структурирована: Таблицы и колонки
  • Fixed Schema: Должно заранее знать структуру
  • Связи: Foreign keys, JOIN'ы
  • ACID: Гарантии надёжности
  • Нормализация: Избежание дублирования (3NF)

Документоориентированная БД (NoSQL)

MongoDB, DynamoDB, Firebase

Структура: Документы (JSON-подобные)

// MongoDB
db.users.insertOne({
  _id: ObjectId('...'),
  name: 'John',
  email: 'john@example.com',
  age: 30,
  address: {
    street: 'Main St',
    city: 'NYC'
  },
  tags: ['developer', 'nodejs'],
  metadata: {
    lastLogin: new Date(),
    loginCount: 42
  }
});

// Один документ может выглядеть как полное представление entity
db.posts.insertOne({
  _id: ObjectId('...'),
  userId: ObjectId('...'),
  title: 'My First Post',
  content: 'Lorem ipsum',
  author: {
    name: 'John',
    email: 'john@example.com'
  },
  comments: [
    {
      userId: ObjectId('...'),
      text: 'Great post!',
      likes: 5
    }
  ]
});

Характеристики:

  • Гибкая schema: Каждый документ может быть разным
  • Вложенность: Можешь вкладывать объекты друг в друга
  • Massively Scalable: Горизонтальное масштабирование
  • BASE: Гарантирует доступность, не консистентность
  • Денормализация: Часто данные дублируются (обратное от SQL)

Таблица Сравнения

┌──────────────────────┬────────────────────────┬─────────────────────────┐
│ Параметр             │ Реляционная (SQL)      │ Документная (NoSQL)      │
├──────────────────────┼────────────────────────┼─────────────────────────┤
│ Структура            │ Таблицы, колонки       │ Документы, поля         │
│ Schema               │ Фиксированная          │ Гибкая                  │
│ Связи                │ Foreign keys, JOIN     │ Вложенность, ссылки     │
│ Нормализация         │ 3NF (минус дублирова) │ Денормализация (плюс    │
│                      │                        │  дублирование)          │
│ Надёжность           │ ACID                   │ BASE                    │
│ Транзакции           │ Да                     │ Ограничено              │
│ Скорость чтения      │ Медленнее (JOIN'ы)     │ Быстрее                 │
│ Масштабируемость     │ Вертикальная           │ Горизонтальная          │
│ Примеры              │ PostgreSQL, MySQL      │ MongoDB, DynamoDB       │
└──────────────────────┴────────────────────────┴─────────────────────────┘

Практический Пример: Блог

Реляционный Подход (PostgreSQL)

-- Нормализация: user, post, comment — отдельные таблицы
CREATE TABLE users (
  id UUID PRIMARY KEY,
  name VARCHAR(100),
  email VARCHAR(100) UNIQUE
);

CREATE TABLE posts (
  id UUID PRIMARY KEY,
  user_id UUID REFERENCES users(id),
  title VARCHAR(255),
  content TEXT
);

CREATE TABLE comments (
  id UUID PRIMARY KEY,
  post_id UUID REFERENCES posts(id),
  user_id UUID REFERENCES users(id),
  text TEXT
);

-- Для получения поста с комментариями и авторами нужны JOIN'ы
SELECT 
  p.*,
  u.name as author_name,
  c.text as comment_text,
  cu.name as commenter_name
FROM posts p
JOIN users u ON p.user_id = u.id
LEFT JOIN comments c ON p.id = c.post_id
LEFT JOIN users cu ON c.user_id = cu.id
WHERE p.id = $1;

Документный Подход (MongoDB)

// Денормализация: всё в одном документе
db.posts.findOne({ _id: ObjectId('...') });

// Результат (одна запись):
{
  _id: ObjectId('...'),
  title: 'My First Post',
  content: 'Lorem ipsum...',
  author: {
    id: ObjectId('...'),
    name: 'John',
    email: 'john@example.com'
  },
  comments: [
    {
      id: ObjectId('...'),
      text: 'Great post!',
      author: {
        id: ObjectId('...'),
        name: 'Jane',
        email: 'jane@example.com'
      }
    }
  ]
}

Сравнение:

  • PostgreSQL: 3 таблицы, нужны JOIN'ы, но нет дублирования
  • MongoDB: 1 документ, готов к отправке в frontend, но дублирование (John's name повторяется в comments)

JOIN'ы в SQL

Проблема: нужно объединять таблицы

SELECT * FROM posts
JOIN users ON posts.user_id = users.id
WHERE posts.id = $1;

-- На больших таблицах JOIN'ы могут быть медленными
-- Нужны индексы: CREATE INDEX idx_posts_user_id ON posts(user_id);

Преимущество: нет дублирования данных

-- Если пользователь изменит имя, обновляем в одном месте
UPDATE users SET name = 'John Updated' WHERE id = $1;
-- Все посты автоматически показывают новое имя при JOIN'е

Денормализация в NoSQL

Преимущество: быстрое чтение (всё в одном документе)

const post = await db.posts.findOne({ _id: postId });
// Сразу всё есть: title, content, author name, comments
// Никакие JOIN'ы не нужны

Недостаток: дублирование

// Если нужно обновить имя автора, нужно обновить:
// 1. В коллекции users
// 2. Во всех документах posts где он автор
// 3. Во всех документах comments где он комментировал

await Promise.all([
  db.users.updateOne({ _id: userId }, { $set: { name: 'New Name' } }),
  db.posts.updateMany({ 'author.id': userId }, { $set: { 'author.name': 'New Name' } }),
  db.comments.updateMany({ 'author.id': userId }, { $set: { 'author.name': 'New Name' } })
]);
// Это может быть медленно и ошибочно

Транзакции

PostgreSQL: ACID транзакции

BEGIN;
  INSERT INTO posts (user_id, title, content) VALUES ($1, $2, $3) RETURNING id;
  UPDATE users SET post_count = post_count + 1 WHERE id = $1;
COMMIT;
-- Если что-то упадёт, всё откатится

MongoDB: Ограниченные транзакции

// Только в MongoDB 4.0+, и только basic transactions
const session = client.startSession();
await session.withTransaction(async () => {
  await db.posts.insertOne({ ... }, { session });
  await db.users.updateOne({ _id: userId }, { $inc: { postCount: 1 } }, { session });
});
// Не рекомендуется для сложных сценариев

Когда Использовать Что

Используй SQL (PostgreSQL), если:

  • Данные структурированы и взаимосвязаны
  • Нужны транзакции (платежи, заказы)
  • Нужна консистентность данных
  • Много JOIN'ов
  • Критична надёжность

Примеры: банки, e-commerce, CRM

Используй NoSQL (MongoDB), если:

  • Данные неструктурированы или меняют schema
  • Нужна высокая масштабируемость
  • Быстрое чтение важнее консистентности
  • Много документов с вложенностью
  • Нужна горизонтальное масштабирование

Примеры: аналитика, логи, кеш, контент-ориентированные приложения

Hybrid Approach

На практике часто используют оба:

// PostgreSQL для критичных данных
const user = await postgres.users.findById(userId);
const order = await postgres.orders.create({ userId, total: 100 });

// MongoDB для аналитики и кеша
await mongodb.analytics.insertOne({ userId, action: 'bought', timestamp: new Date() });
await mongodb.cache.updateOne({ _id: 'user_profile' }, { $set: { ... } });

// Redis для сессий и rate limiting
await redis.setex(`session:${sessionId}`, 3600, JSON.stringify(sessionData));

Мой Выбор в Production

По умолчанию: PostgreSQL (SQL)

  • Большинство приложений имеют структурированные данные
  • ACID гарантии стоят свечи
  • JOIN'ы это не проблема с правильными индексами

Когда переходить на MongoDB:

  • Явное требование на высокую масштабируемость
  • Схема часто меняется (MVP phase)
  • Данные неструктурированы (логи, аналитика)

Никогда не выбираю по трендам: "NoSQL лучше" — неправда. Выбираю по требованиям.

Заключение

SQL: структурированно, надёжно, но медленнее NoSQL: быстро, масштабируемо, но гибче

Выбор зависит от:

  • Структуры данных
  • Требований к консистентности
  • Требований к масштабируемости
  • Опыта команды

Лучшие backend разработчики знают оба подхода и выбирают инструмент по задаче, а не по тренду.