Теоретический конспект | ОП.08 Базы данных

Тема 4. Проектирование структур баз данных — Средства проектирования структур БД: объекты

Учебный конспект для обучающихся СПО (СИЭУиП) | Семестр 4, Занятие №21

1. Введение: от реляционной алгебры к системным объектам СУБД

В 3 семестре вы изучили реляционную модель данных, правила нормализации отношений от 1NF до BCNF, основы SQL-запросов и построение первичных индексов. На этом этапе база данных рассматривалась преимущественно как совокупность таблиц (отношений) и связей между ними.

Однако в реальных промышленных информационных системах (банковских АБС, медицинских архивах PACS, логистических центрах, ERP) база данных представляет собой высокоорганизованный комплекс взаимосвязанных объектов, управляемых системным каталогом СУБД. Грамотное проектирование требует применения специализированных объектов, решающих задачи изоляции подсистем, разграничения доступа, кэширования ресурсоемких расчетов и потоковой генерации уникальных ключей.

2. Иерархия объектов в системном каталоге реляционной СУБД

Каждая промышленная реляционная СУБД (PostgreSQL, Oracle, Microsoft SQL Server) организует пространство хранения в виде строгой многоуровневой иерархии:

Архитектурная блок-схема иерархии объектов СУБД:
[ СУБД КЛАСТЕР (Database Cluster / Instance) ] |-- Общие ресурсы: Shared Buffers, WAL, Табличные пространства (Tablespaces) |-- Глобальные объекты безопасности: Роли и Пользователи (ROLES / USERS) | +-- [ БАЗА ДАННЫХ A (Database) ] | |-- Системный каталог (information_schema, pg_catalog) | | | +-- [ СХЕМА / ПРОСТРАНСТВО ИМЕН: public ] | | +-- Таблицы (TABLES) | | +-- Индексы (INDEXES) | | | +-- [ СХЕМА / ПРОСТРАНСТВО ИМЕН: billing ] | | +-- Таблицы (TABLES): subscribers, accruals, payments | | +-- Представления (VIEWS): v_active_subscribers | | +-- Материализованные витрины (MATVIEWS): mv_monthly_debt_summary | | +-- Последовательности (SEQUENCES): receipt_seq, invoice_seq | | +-- Пользовательские типы (TYPES): payment_status_enum | | | +-- [ СХЕМА / ПРОСТРАНСТВО ИМЕН: audit ] | +-- Партиционированные таблицы истории изменений | +-- [ БАЗА ДАННЫХ B (Database) ] +-- ... (полная логическая изоляция)

Разбор уровней иерархии:

Управление пространством поиска: Переменная сессии search_path определяет приоритет схем при обращении к объекту без явного квалификатора (например, просто SELECT * FROM clients вместо SELECT * FROM billing.clients). Команда: SET search_path TO billing, public;

3. Последовательности (SEQUENCE) и генераторы первичных ключей

SEQUENCE (Последовательность) — это специализированный объект базы данных, предназначенный для генерации неповторяющейся числовой последовательности (чаще всего целочисленной: INTEGER или BIGINT).

Зачем нужна отдельная SEQUENCE вместо MAX(id) + 1?

Наивная попытка вычислить следующий идентификатор через SELECT MAX(id) + 1 FROM table приводит к двум катастрофическим проблемам:

  1. Гонка условий (Race Condition): при одновременной вставке двух транзакций они прочитают одинаковое значение MAX(id), что приведет к нарушению ограничения уникальности PRIMARY KEY.
  2. Падение производительности: блокировка всей таблицы ради вычисления максимума превращает многопоточную систему в медленную однопоточную очередь.

Объект SEQUENCE работает вне транзакционного контекста через атомарные счетчики в оперативной памяти (Shared Memory):

-- Создание независимой последовательности
CREATE SEQUENCE billing.receipt_seq
    START WITH 100000
    INCREMENT BY 1
    MINVALUE 100000
    MAXVALUE 999999999
    CACHE 20;

-- Основные функции работы с последовательностью:
-- 1. nextval('receipt_seq') - атомарно инкрементирует и возвращает новое значение.
-- 2. currval('receipt_seq') - возвращает текущее значение в рамках ДАННОЙ пользовательской сессии.
-- 3. setval('receipt_seq', 500000) - устанавливает счетчик на заданное число.
Важное свойство: Если транзакция, выполнившая nextval(), завершилась с ошибкой или была отменена (ROLLBACK), сгенерированное число не возвращается обратно в последовательность. В первичных ключах возникают допустимые пропуски (Gaps). Последовательности проектируются ради скорости и уникальности, а не непрерывности.

4. Представления: Динамические (VIEW) против Материализованных (MATERIALIZED VIEW)

Представление (View) в SQL — это именованный сохраненный запрос. В современной инженерии баз данных существует два принципиально разных вида представлений:

Параметр сравнения Обычное представление (VIEW) Материализованное представление (MATERIALIZED VIEW)
Принцип работы Виртуальная таблица. Текст SQL-запроса подставляется оптимизатором в абстрактное синтаксическое дерево (AST) клиентского запроса. Физическая таблица-снапшот. Результат запроса однократно вычисляется и физически записывается на диск в блоки данных.
Занимаемая память на диске 0 байт (хранится только мета-описание запроса в системном каталоге pg_views). Полноценный объем данных (включая дисковое пространство под созданные индексы).
Свежесть данных Мгновенная (всегда отражает состояние базовых таблиц на микросекунду выполнения). Статичный снимок на момент выполнения команды REFRESH. Данные могут отставать от оперативной базы.
Возможность индексации Невозможно создать индекс на сам VIEW. Поддерживает создание любых индексов (B-Tree, Hash, BRIN, GIN) прямо по вычисленным столбцам!
Скорость выполнения Зависит от сложности исходного запроса (JOIN, подзапросы, агрегаты). Медленно на больших объемах. Экстремально высокая (O(log N)) при наличии индексов, так как данные уже предвычислены.
Целевое назначение Маскирование персональных данных, изоляция столбцов для ролей, упрощение сложных бизнес-выборок. Аналитические отчеты (OLAP), дашборды руководителей, агрегация миллионов транзакций за сутки/месяцы.

Синтаксис и регламент обновления MATERIALIZED VIEW:

-- 1. Создание материализованной витрины
CREATE MATERIALIZED VIEW billing.mv_monthly_debt_summary AS
SELECT 
    s.district_id,
    date_trunc('month', a.accrual_date)::date AS billing_month,
    count(DISTINCT s.subscriber_id) AS active_subscribers_count,
    sum(a.amount) AS total_accrued_rub,
    sum(coalesce(p.paid_amount, 0)) AS total_paid_rub,
    sum(a.amount - coalesce(p.paid_amount, 0)) AS total_debt_rub
FROM billing.subscribers s
JOIN billing.accruals a ON s.subscriber_id = a.subscriber_id
LEFT JOIN (
    SELECT accrual_id, sum(amount) AS paid_amount
    FROM billing.payments
    GROUP BY accrual_id
) p ON a.accrual_id = p.accrual_id
GROUP BY s.district_id, date_trunc('month', a.accrual_date);

-- 2. Создание обязательного УНИКАЛЬНОГО индекса для фонового обновления
CREATE UNIQUE INDEX idx_mv_debt_pk 
ON billing.mv_monthly_debt_summary (district_id, billing_month);

-- 3. Регламентное обновление данных:
-- Обычное обновление (блокирует чтение витрины эксклюзивным замком):
-- REFRESH MATERIALIZED VIEW billing.mv_monthly_debt_summary;

-- Конкурентное фоновое обновление (клиенты продолжают читать старый снимок без задержек):
REFRESH MATERIALIZED VIEW CONCURRENTLY billing.mv_monthly_debt_summary;

5. Доменные типы данных: ENUM и составные структуры

Реляционная модель требует атомарности значений (1NF). Однако введение перечислимых и составных типов на уровне СУБД повышает надежность модели данных и предотвращает запись мусорных строковых значений:

-- Перечислимый тип для статусов логистических отправлений
CREATE TYPE logistics.parcel_status AS ENUM (
    'REGISTERED',
    'IN_TRANSIT',
    'ARRIVED_AT_HUB',
    'OUT_FOR_DELIVERY',
    'DELIVERED',
    'RETURNED'
);

-- Составной тип для географических координат
CREATE TYPE logistics.coordinates AS (
    lat NUMERIC(9, 6),
    lon NUMERIC(9, 6)
);

6. CASE-инструменты визуального проектирования и DDL-инженерия

В современной промышленной разработке проектирование архитектуры СУБД осуществляется в специализированных CASE-пакетах (Computer-Aided Software Engineering):

Концепции Forward и Reverse Engineering:

Вопросы для самопроверки и закрепления знаний:
  1. В чем заключается фундаментальное архитектурное различие между кластером СУБД, базой данных и схемой?
  2. Почему при откате транзакции (ROLLBACK) счетчик SEQUENCE не возвращается назад? К каким последствиям это привело бы в высоконагруженной системе?
  3. В каких бизнес-ситуациях архитектурно оправдано использование MATERIALIZED VIEW вместо обычного VIEW? Назовите минимум 2 фактора.
  4. Какое техническое условие является строго обязательным для выполнения команды REFRESH MATERIALIZED VIEW CONCURRENTLY?
  5. Чем отличается концепция Forward Engineering от Reverse Engineering в CASE-инструментах?