Теоретические основы реляционных данных
Play ITЗагрузка интерактивного демо…
Разработчику
Аналитику
Тестировщику
Архитектору
Инженеру
Теория данных
Эта глава — "под капотом" реляционной СУБД: от модели Кодда до страниц на диске, WAL и реляционной алгебры. Если знакомство отвечает на "что такое БД", а внутреннее устройство — на "как летит запрос", здесь собран скелет теории, на который опираются SQL, проектирование и администрирование.
Критерии "настоящей" реляционной системы — в отдельной главе Двенадцать правил Кодда. После чтения про WAL логично перейти к Восстановлению после сбоя и к резервному копированию.
Play ITЗагрузка интерактивного демо…
Основы теории информатики в части данных
Теория данных вытекает из более общей теории информации, предложенной Клодом Шенноном, и из теории вычислимости, развитой Алонзо Чёрчем, Алланом Тьюрингом и другими. Однако в контексте информационных систем данные рассматриваются как структурированные объекты, подлежащие манипуляции в рамках строго определённых моделей.
Ключевое понятие — данные как представление знаний. Любая система хранения данных предполагает наличие модели, определяющей:
- какие типы данных допустимы;
- как данные связаны между собой;
- как над ними могут быть выполнены операции;
- как обеспечивается целостность и согласованность.
В теории информатики данные формализуются через понятие реляции (в рамках реляционной модели), графа (в графовых базах), документа (в документ-ориентированных системах) или ключа-значения (в key-value хранилищах). Эти модели не являются взаимоисключающими — они отражают различные уровни абстракции и подходы к организации информации в зависимости от требований приложения.
Наиболее строго и формально развита реляционная модель данных (РМД), предложенная Эдгаром Ф. Коддом в 1969–1970 годах (статья A Relational Model of Data for Large Shared Data Banks). Она опирается на теорию множеств и логику первого порядка и остаётся основой большинства промышленных СУБД.
Состав реляционной модели данных
РМД традиционно описывают тремя составляющими:
| Составляющая | Содержание |
|---|---|
| Структурная | Данные — набор отношений (переменных отношений во времени) |
| Целостности | Отношения удовлетворяют ограничениям уровня домена, отношения и всей БД |
| Обработки (манипуляции) | Реляционная алгебра и реляционное исчисление — операторы над отношениями |
К модели относят также теорию нормализации (устранение избыточности и аномалий обновления).
Термин "реляционный" — от англ. relation ("отношение"), математического понятия подмножества декартова произведения доменов. Слово "таблица" в разговоре — лишь визуализация отношения; отношение абстрактно и не бывает "плоским" или "двумерным" — такими бывают только рисунки на экране.
Три опорных факта (по Кодду и Дейту):
- Модель логическая — отношения абстрактны, отдельно от физического размещения на диске.
- Информационный принцип — всё содержимое БД представлено явными значениями в кортежах; нет скрытых указателей между значениями (в отличие от сетевой навигации по ссылкам).
- Наличие алгебры даёт декларативные запросы и ограничения целостности поверх процедурного доступа.
Исторические альтернативы — иерархическая и сетевая модели; обзор — в Знакомстве с БД.
Отношение — заголовок, тело, ключи
Отношение степени n — подмножество декартова произведения доменов T₁…Tₙ. Состоит из:
- Заголовка (схемы) — конечного множества пар (имя атрибута, домен/тип); имена атрибутов в одном отношении уникальны.
- Тела — множества кортежей; каждый кортеж — набор значений по атрибутам заголовка.
Свойства отношения (в строгой теории):
- кортежи не повторяются;
- порядок кортежей не определён;
- порядок атрибутов в заголовке не определён.
Потенциальный ключ — подмножество атрибутов с уникальностью и минимальностью (нельзя убрать атрибут без потери уникальности). В каждом отношении есть хотя бы один такой ключ.
К. Дж. Дейт формулирует пять условий, при которых таблица на экране — корректное представление отношения: нет упорядоченности строк и столбцов как носителя смысла; нет дубликатов строк; в каждой ячейке одно значение из домена; нет "скрытых" столбцов (суррогатных OID строк, недоступных в обычном SQL).
В SQL таблица близка к отношению, но стандарт допускает дубликаты строк, порядок столбцов в выражениях может иметь значение, возможны безымянные столбцы — поэтому говорят о SQL-таблицах, а не о чистых отношениях.
Целостность базы данных
Целостность БД — соответствие данных внутренней логике, структуре и явно объявленным правилам. Каждое правило — ограничение целостности; СУБД отклоняет операции, которые его нарушают, и проверяет БД при добавлении нового ограничения.
В реляционной теории выделяют уровни ограничений:
| Уровень | Пример |
|---|---|
| Тип (домен) | Множество допустимых значений атрибута |
| Атрибут | CHECK (возраст >= 0) |
| Переменная отношения | потенциальный ключ, UNIQUE |
| База данных | внешний ключ между отношениями |
Целостность и истинность данных — разные вещи. СУБД гарантирует непротиворечивость (нет "висячих" FK, отрицательного веса, если так запрещено), но не знает реальный мир: в БД может лежать ошибочный, но формально допустимый факт. Проверку смысла (верны ли остатки на складе) обеспечивают приложение, процессы и люди.
Подробнее про внешние ключи в проектировании — Entity Relationship; про ссылочную целостность на диске — раздел ниже в этой главе.
Согласованность данных
Согласованность данных (англ. data consistency, в русскоязычных текстах иногда консистентность) — согласованность значений друг с другом, целостность и внутренняя непротиворечивость набора данных.
| Контекст | Что задаёт согласованность |
|---|---|
| ER-модель | Допустимые значения атрибутов сущностей; какие связи можно устанавливать; минимальное и максимальное число связей данного типа для одного узла |
| Реляционная БД | Ограничения целостности (ключи, домены, CHECK, внешние ключи) — см. таблицу выше |
| Структуры данных в коде | Инварианты структуры: у BST — отсутствие циклов, порядок ключей, согласованность указателей parent с left/right |
| ООП | Инкапсуляция: операции над объектом не выводят его из согласованного состояния |
Целостность в СУБД — частный и формализованный случай согласованности: правила объявлены в схеме и проверяются движком. Согласованность в распределённых системах (реплики, кэши, микросервисы) шире одной транзакции — см. конкурентный доступ и материалы про eventual consistency в разделе микросервисов.
Случайные байты в массиве фиксированной длины дают набор чисел без "поломки" структуры; в строке UTF-8 те же байты могут нарушить кодировку — пример, почему согласованность привязана к модели данных, а не только к объёму памяти.
Как работает база данных и СУБД на самом низшем уровне
Система управления базами данных (СУБД) — это программный комплекс, предоставляющий интерфейс для хранения, извлечения, модификации и управления данными с гарантией их целостности, безопасности и производительности. На самом низком уровне СУБД — это набор алгоритмов и структур данных, которые взаимодействуют с операционной системой и, через неё, с аппаратными ресурсами вычислительной машины.
Основные компоненты СУБД на уровне реализации:
- Менеджер буферов (Buffer Manager) — отвечает за перемещение данных между оперативной памятью и диском.
- Менеджер дискового пространства (Storage Manager) — управляет физическим расположением данных на диске, включая файлы, страницы и блоки.
- Менеджер транзакций (Transaction Manager) — обеспечивает ACID-свойства (атомарность, согласованность, изолированность, долговечность).
- Менеджер выполнения запросов (Query Execution Engine) — преобразует логические операции (например,
SELECT) в последовательность физических действий над данными. - Оптимизатор запросов (Query Optimizer) — выбирает наиболее эффективный план выполнения запроса на основе статистики и стоимости операций.
На аппаратном уровне данные хранятся в виде последовательностей байтов на энергонезависимом носителе — традиционно на жёстком диске (HDD) или твердотельном накопителе (SSD). СУБД использует системные вызовы операционной системы (read, write, mmap и др.) для доступа к файлам базы данных.
Где хранятся данные (на диске)
Физическое хранение данных в реляционной СУБД обычно организовано по принципу страничной структуры. База данных представляет собой один или несколько файлов, разделённых на фиксированные блоки — страницы (pages), типичный размер которых составляет 4 КБ, 8 КБ или 16 КБ, в зависимости от СУБД (например, PostgreSQL использует 8 КБ, Microsoft SQL Server — 8 КБ по умолчанию).
Каждая страница содержит:
- заголовок (метаданные — тип страницы, идентификатор отношения, контрольные суммы и т.п.);
- тело — фрагменты записей (tuples или rows);
- иногда — слот-массив, указывающий смещения записей внутри страницы.
Записи (rows) не обязательно хранятся последовательно — они могут фрагментироваться, особенно при обновлении переменной длины. Для поддержки целостности и быстрого доступа используется индексная структура, чаще всего — сбалансированное дерево (B+ дерево), которое также хранится на диске в виде страниц.
Важно понимать: диск — это иерархический, последовательный по природе носитель. Даже в SSD, где отсутствует механический seek, эффективность чтения/записи зависит от выравнивания блоков и внутренней архитектуры контроллера NAND-памяти. Поэтому СУБД стремятся минимизировать количество случайных обращений и максимизировать локальность данных.
Как СУБД работает с диском
Когда приходит SQL-запрос, например SELECT * FROM users WHERE id = 100, СУБД не читает всю таблицу с диска целиком. Вместо этого она:
- Анализирует запрос и строит его логическое представление (предикаты, соединения и т.п.).
- Оптимизатор выбирает план выполнения — например, использовать индекс по полю
id. - Менеджер выполнения инициирует чтение нужных страниц с диска через менеджер буферов.
- Менеджер буферов проверяет, есть ли запрашиваемая страница в оперативной памяти (в буферном пуле). Если нет — выполняется системный вызов для чтения с диска.
- Прочитанная страница кэшируется в памяти, чтобы последующие запросы к тем же данным не требовали повторного обращения к диску.
Таким образом, работа с диском происходит лениво и выборочно — только те страницы, которые действительно нужны для выполнения запроса, загружаются в память. Это критически важно, поскольку объём данных на диске может значительно превышать объём оперативной памяти.
СУБД также использует запись журналов (Write-Ahead Logging, WAL) — перед тем как изменить данные на диске, СУБД записывает изменения в специальный лог (журнал транзакций), что позволяет восстановить согласованное состояние после сбоя.
Почему базы данных предпочтительнее хранения в ОЗУ
Хранение данных исключительно в оперативной памяти (in-memory) возможно и используется в специализированных системах (например, Redis, SingleStore — ранее MemSQL, in-memory OLTP в SQL Server). Однако классические СУБД с дисковым хранением обладают рядом фундаментальных преимуществ:
- Долговечность (Durability): данные сохраняются после выключения питания или аварийного завершения работы. ОЗУ — энергозависимый ресурс.
- Масштабируемость по объёму: объём диска измеряется терабайтами и петабайтами, тогда как ОЗУ ограничено физическими и экономическими рамками.
- Контроль целостности — СУБД реализует сложные механизмы (ограничения, внешние ключи, триггеры, ACID), которые невозможно или нецелесообразно воспроизводить вручную в пользовательском коде.
- Конкуренция и изоляция — СУБД управляет параллельным доступом множества клиентов к данным, обеспечивая корректность через механизмы блокировок, MVCC (многоверсионного управления конкурентным доступом) и т.д.
- Оптимизированная работа с носителем — СУБД знают структуру данных и могут эффективно использовать индексы, кластеризацию, сжатие и предвыборку (prefetching), что недоступно при простом хранении в памяти.
In-memory хранилища — это частный случай, оправданный при строгих требованиях к задержке, но они не заменяют дисковые СУБД в общем случае.
Реляционная алгебра и реляционное исчисление
Таблицу как множество кортежей и операции ∪, ∩, разность, декартово произведение над отношениями разбирают в статье Реляционная алгебра и таблицы.
Ниже — алгебра Кодда (σ, π, ⋈), на которой строится оптимизатор SQL.
Реляционная модель данных, предложенная Эдгаром Коддом, покоится на двух формальных основаниях: реляционной алгебре и реляционном исчислении. Эти две системы скорее дополняют друг друга: первая ориентирована на как вычислять результат, вторая — на что должно быть в результате.
Реляционная алгебра — это процедурный формализм. Она состоит из набора операторов, которые принимают одно или несколько отношений (таблиц) и возвращают новое отношение. Основные операторы включают:
- Выборку (selection) — фильтрация строк по условию;
- Проекцию (projection) — выбор подмножества столбцов;
- Декартово произведение — комбинирование всех строк двух отношений;
- Соединение (join) — комбинирование строк с учётом условия соответствия;
- Объединение, пересечение и разность — теоретико-множественные операции над совместимыми по структуре отношениями;
- Переименование атрибутов — изменение имён столбцов без изменения содержания.
Каждая операция алгебры строго определена и всегда возвращает корректное отношение. Это позволяет строить планы выполнения запросов как последовательности этих операций. Именно реляционная алгебра лежит в основе внутреннего представления SQL-запросов в большинстве СУБД — парсер SQL преобразует запрос в древовидную структуру, соответствующую выражению реляционной алгебры, которое затем оптимизируется и исполняется.
Реляционное исчисление, в свою очередь, основано на логике предикатов первого порядка. Оно позволяет формулировать запросы в декларативной форме — указывается, какие кортежи должны присутствовать в результате, через логические условия, которым они должны удовлетворять. Например, "найти все строки, для которых существует запись в другой таблице с таким же идентификатором". В отличие от алгебры, здесь не задаётся последовательность шагов — только условие результата.
Ключевой теоретический результат — эквивалентность реляционной алгебры и реляционного исчисления в выразительной силе. Это означает, что любой запрос, выразимый в одной системе, может быть выражен и в другой. Более того, Кодд ввёл понятие реляционной полноты: СУБД считается реляционно полной, если она способна реализовать все операции реляционной алгебры. SQL, несмотря на некоторые отклонения от чистой теории (например, допускает дубликаты строк и отсутствие полной поддержки всех свойств отношений), в своей основе соответствует этому требованию.
Эти формальные основы обеспечивают предсказуемость и корректность обработки данных. Пользователь описывает что ему нужно, а СУБД берёт на себя задачу как это получить, используя мощный аппарат оптимизации на основе алгебраических преобразований и статистики данных.
Как вычисляются связи на низком уровне
Термин "связи" в контексте баз данных чаще всего относится к внешним ключам (foreign keys) — ограничениям целостности, устанавливающим, что значение в одном столбце (или наборе столбцов) одной таблицы должно соответствовать значению первичного ключа в другой таблице.
На логическом уровне это выглядит как декларативное правило: "значение столбца user_id в таблице orders должно существовать в столбце id таблицы `users". Однако на физическом уровне реализация этого правила требует конкретных механизмов.
Проверка внешнего ключа выполняется СУБД в момент модификации данных:
- При вставке строки в дочернюю таблицу (
orders) СУБД ищет соответствующую запись в родительской таблице (users). Это поиск по индексу первичного ключа — операция, в большинстве случаев логарифмическая по времени. - При удалении или обновлении строки в родительской таблице СУБД проверяет, ссылаются ли на неё какие-либо строки в дочерних таблицах. В зависимости от политики (например,
CASCADE,RESTRICT,SET NULL) могут быть выполнены дополнительные действия.
Важно: сами связи не хранятся как отдельные объекты на диске. Нет "ссылок" в том смысле, в каком они существуют в памяти (например, указатели). Связь — это логическое ограничение, реализуемое через:
- наличие индексов по столбцам внешнего и первичного ключей (для эффективного поиска);
- выполнение проверок при операциях изменения данных;
- поддержание согласованности при восстановлении после сбоев (через WAL-журналы).
При выполнении операции соединения (JOIN) СУБД физически комбинирует строки из двух таблиц, сопоставляя значения в соответствующих столбцах. Существует несколько алгоритмов соединения:
- Вложенные циклы (nested loops) — для каждой строки из левой таблицы просматриваются все строки правой;
- Соединение по хешу (hash join) — строится хеш-таблица по одной таблице, затем вторая таблица сканируется и сверяется по хешу;
- Сортировка и слияние (sort-merge join) — обе таблицы сортируются по ключу соединения, затем выполняется линейное слияние.
Выбор алгоритма зависит от объёма данных, наличия индексов, доступной памяти и статистики распределения значений. Все эти алгоритмы оперируют страницами данных, загружаемыми в буферный пул, и стремятся минимизировать количество операций ввода-вывода.
Таким образом, связи в реляционной модели — это логические зависимости, поддерживаемые через индексы, алгоритмы соединения и механизмы контроля целостности, реализованные на уровне СУБД.
См. также
- Двенадцать правил Кодда
- Роль базы данных в организации
- Конкурентный доступ
- Восстановление после сбоя
- Реляционная модель