Databases

Модели данных баз данных с примерами промышленных СУБД.

Модель данных — это способ логического описания, организации и хранения информации в базе данных. Она определяет структуру данных, связи между ними и методы доступа.

Существует несколько основных моделей:

Реляционная модель представляет данные в виде таблиц со строками и столбцами. Связи между таблицами обеспечиваются ключами — первичными и внешними. Примеры: PSQL, MySQL, Oracle, MSSQL.

Иерархическая модель строит данные как дерево: один родитель → много детей. Пример: IBM IMS.

Сетевая модель расширяет иерархическую, позволяя множество связей между записями. Пример: IDMS.

NoSQL объединяет несколько подходов, оптимизированных под разные задачи:

Нормализация (нормальные формы) в реляционной модели данных.

Нормализация — процесс удаления избыточных данных, устранение аномалий.

Цели

Избыточность данных — когда одни и те же данные хранятся в нескольких местах базы данных.

Процесс нормализации — последовательный процесс приведения базы данных к эталонному виду, переход от одной нормальной формы к следующей.

Нормальная форма базы данных — набор правил и критериев, которым должна соответствовать база данных.


UNF (Ненормальная форма, нулевая нормальная форма)

Перед приведением БД к нормальной форме нужно привести её к табличному виду, чтобы он соответствовал базовым принципам реляционной теории:

Пример таблицы в UNF

BlockID Area Operator Holes Depths Explosives Charges
B1 A1 Op1 H1,H2 10,12 ANFO, Emulsion 50,60

Проблемы:


1NF (Первая нормальная форма)

Правила 1NF:

Пример таблицы в 1NF

BlockID Area Operator HoleID Depth Explosive Charge
B1 A1 Op1 H1 10 Березит 50
B1 A1 Op1 H2 12 Амонит 60

2NF (Вторая нормальная форма)

Правила 2NF:

Примеры таблиц в 2NF

BlastBlock

BlockID AreaID OperatorID
B1 A1 O1

Area

AreaID AreaName
A1 Pit-1

Operator

OperatorID OperatorName
O1 Опер главный

BlastHole

HoleID BlockID Depth
H1 B1 10
H2 B1 12

Charging

HoleID ExplosiveID Charge
H1 E1 50
H2 E2 60

ExpAgent

ExplosiveID ExplosiveName
E1 Березит
E2 Амонит

3NF (Третья нормальная форма)

Правила 3NF:

Примеры в наших таблицах:


BCNF (Нормальная форма Бойса–Кодда)

Правила BCNF:

То есть, не должно быть зависимостей, где неключевой столбец определяет другой столбец.

Первичный ключ, внешний ключ, отношения.

Первичный ключ — это поле или совокупность полей таблицы, которое однозначно идентифицирует каждую строку. Значение первичного ключа не должно повторяться и, как правило, не может быть NULL. Именно первичный ключ позволяет надежно ссылаться на конкретную запись.

Внешний ключ — это поле или группа полей в одной таблице, которое ссылается на первичный ключ другой таблицы. Внешние ключи используются для обеспечения ссылочной целостности данных и описывают связи между таблицами.

Отношения между таблицами

В реляционных системах управления базами данных (РСУБД) таблицы связываются между собой с помощью первичных (PK) и внешних (FK) ключей. Тип связи определяет, сколько записей одной таблицы может соответствовать записям другой.

Связь «один ко многим»

Одна запись в родительской таблице может быть связана с множеством записей в дочерней таблице.

Пример. Одна область (Area) может содержать множество блоков (BlastBlocks). В таблице BlastBlock присутствует колонка AreaId, которая является внешним ключом и ссылается на первичный ключ таблицы областей.

Связь «один к одному»

Каждая запись в одной таблице связана ровно с одной записью в другой таблице.

Пример. Один сотрудник связан с одним служебным пропуском. В одной из таблиц внешний ключ имеет ограничение уникальности и ссылается на первичный ключ другой таблицы.

Связь «многие ко многим»

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

Такую связь невозможно реализовать напрямую, поэтому используется промежуточная таблица. Она содержит два внешних ключа — по одному на каждую из связанных таблиц.

Пример. Студент может посещать множество курсов, и на один курс могут быть записаны многие студенты. Промежуточная таблица хранит пары идентификаторов студентов и курсов.

Язык SQL (SELECT, WHERE, (LEFT) JOIN, GROUP BY, HAVING).

DDL и DML — подмножество языка SQL

DDL (Data Definition Language, язык описания данных) служит для создания и модификации структуры БД, т.е. для создания/изменения/удаления таблиц и связей.

DML (Data Manipulation Language, язык манипулирования данными) позволяет осуществлять манипуляции с данными таблиц, т.е. с ее строками. Он позволяет делать выборку данных из таблиц, добавлять новые данные в таблицы, а также обновлять и удалять существующие данные.


SQL

язык запросов для управления данными в РСУБД.


SELECT

оператор для выбора данных из одной или нескольких таблицы

SELECT <columns> FROM <table>

WHERE

оператор для фильтрации результатов

SELECT <columns> FROM <table> WHERE <condition>

GROUP BY

оператор для группировки по определенному столбцу

SELECT <columns> FROM <table> GROUP BY <column>
SELECT Department, COUNT(*) as EmployeeCount FROM Employees GROUP BY Department

HAVING

позволяет ставить условия на группы, которые были получены после GROUP BY.

Это как WHERE, но для сгруппированных данных

SELECT Country, COUNT(*) AS cnt
FROM Customers
GROUP BY Country
HAVING COUNT(*) > 10;

WHERE — фильтрует исходные строки перед группировкой. HAVING — фильтрует уже сгруппированные результаты.

LEFT JOIN

выводит все строки из левой таблицы, даже если в правой нет совпадений. Если нет совпадающих данных — связанные поля будут NULL.

SELECT c.CustomerName, o.OrderID
FROM Customers c
LEFT JOIN Orders o
  ON c.CustomerID = o.CustomerID;

Последовательность логической обработки SQL-запроса

FROM / JOIN → WHERE → GROUP BY → HAVING → SELECT

То есть:

Язык SQL (DML: INSERT, UPDATE, DELETE). Варианты синтаксиса для множественного обновления данных.

DDL и DML — подмножество языка SQL

DDL (Data Definition Language, язык описания данных) служит для создания и модификации структуры БД, т.е. для создания/изменения/удаления таблиц и связей.

DML (Data Manipulation Language, язык манипулирования данными) позволяет осуществлять манипуляции с данными таблиц, т.е. с ее строками. Он позволяет делать выборку данных из таблиц, добавлять новые данные в таблицы, а также обновлять и удалять существующие данные.

INSERT

Оператор используется для добавления данных в таблицу.

Добавление одной строки:

INSERT INTO Employees (Name, Department, Salary)
VALUES ('Ivan', 'IT', 1000);

Добавление нескольких строк (множественная вставка):

INSERT INTO Employees (Name, Department, Salary)
VALUES
  ('Ivan', 'IT', 1000),
  ('Anna', 'HR', 900),
  ('Petr', 'IT', 1100);

Вставка данных из другой таблицы:

INSERT INTO Employees (Name, Department, Salary)
SELECT Name, Department, Salary
FROM NewEmployees;

UPDATE

Оператор используется для изменения существующих данных.

Обновление нескольких строк по условию:

UPDATE Employees
SET Salary = Salary * 1.1
WHERE Department = 'IT';

В SQL нет отдельного оператора для обновления «одной» или «нескольких» строк — количество обновляемых строк определяется условием WHERE.

Множественное обновление нескольких столбцов:

UPDATE Employees
SET
  Salary = 1200,
  Department = 'Finance'
WHERE Name = 'Ivan';

Обновление с использованием подзапроса:

UPDATE Employees
SET Salary = (
    SELECT AVG(Salary)
    FROM Employees
)
WHERE Department = 'HR';

Обновление с JOIN (диалектозависимо, например MySQL / PostgreSQL):

UPDATE Employees e
SET Salary = s.NewSalary
FROM SalaryChanges s
WHERE e.EmployeeID = s.EmployeeID;

DELETE

Оператор используется для удаления данных.

Удаление строк по условию:

DELETE FROM Employees
WHERE Department = 'HR';

Удаление всех строк таблицы:

DELETE FROM Employees;

Удаление с использованием подзапроса:

DELETE FROM Employees
WHERE Salary < (
    SELECT AVG(Salary)
    FROM Employees
);

Summary:

Множественное обновление данных в SQL достигается:

Язык SQL (Триггеры, процедуры, функции, а также курсоры, циклы, условные операторы, временные таблицы).

Триггер

хранимая процедура, которая автоматически выполняется в ответ на определённые события в базе данных

События:

Время срабатывания:

Пример триггера:

CREATE TRIGGER <name_trigger>
BEFORE INSERT ON <table_name>
FOR EACH ROW
BEGIN
    SET NEW.created_at = NOW();
END;

Хранимые процедуры

набор SQL-операторов, сохранённых в БД и выполняемых по явному вызову

Особенности:

Пример:

CREATE PROCEDURE add_user(IN p_name VARCHAR(50))
BEGIN
    INSERT INTO users(name) VALUES (p_name);
END;

-- Вызов процедуры
CALL add_user('Ivan');

Функции

подпрограмма, которая обязательно возвращает значение

Особенности:

Пример:

CREATE FUNCTION get_user_count()
RETURNS INT
BEGIN
    RETURN (SELECT COUNT(*) FROM users);
END;

SELECT get_user_count();

Курсор

механизм для построчной обработки результата запроса, когда невозможно обработать данные одним SQL-запросом

Используется:

Основные шаги работы с курсором:

  1. объявление курсора
  2. открытие
  3. чтение строк (FETCH)
  4. закрытие
  5. освобождение ресурсов

Пример:

-- Объявление
DECLARE cur CURSOR FOR
SELECT id FROM users;

-- Открытие
OPEN cur;

-- Чтение строк
FETCH NEXT FROM cur;

-- Закрытие
CLOSE cur;

-- Освобождение ресурсов
DEALLOCATE cur;

Циклы

В SQL (в хранимых программах) используются следующие циклы:

DECLARE counter INT DEFAULT 1;

WHILE counter <= 5 DO
    INSERT INTO numbers(value) VALUES (counter);
    SET counter = counter + 1;
END WHILE;
DECLARE counter INT DEFAULT 1;

REPEAT
    INSERT INTO numbers(value) VALUES (counter);
    SET counter = counter + 1;
UNTIL counter > 5
END REPEAT;
DECLARE counter INT DEFAULT 1;

my_loop: LOOP
    IF counter > 5 THEN
        LEAVE my_loop;
    END IF;
    INSERT INTO numbers(value) VALUES (counter);
    SET counter = counter + 1;
END LOOP my_loop;

Условные операторы

Для ветвления логики применяются:

DECLARE user_age INT DEFAULT 20;

IF user_age >= 18 THEN
    SET @status = 'Adult';
ELSE
    SET @status = 'Minor';
END IF;
SELECT
    name,
    CASE
        WHEN salary < 1000 THEN 'Low'
        WHEN salary BETWEEN 1000 AND 3000 THEN 'Medium'
        ELSE 'High'
    END AS salary_level
FROM employees;

Временные таблицы

таблица для временного хранения данных

-- Создание временной таблицы
CREATE TEMPORARY TABLE temp_users (
    id INT,
    name VARCHAR(50)
);

-- Вставка данных
INSERT INTO temp_users VALUES (1, 'Ivan'), (2, 'Anna');

-- Использование временной таблицы
SELECT * FROM temp_users;

-- После окончания сессии таблица автоматически удаляется

Особенности:

Индексы в РСУБД, виды индексов.

Индекс — специальная структура данных, предназначенная для ускорения поиска и сортировки данных в таблицах. Создается по одному или нескольким столбцам таблицы и хранит:

Аналогия: индекс в книге позволяет быстро найти нужную страницу, не просматривая всю книгу

Назначение индексов:

Недостатки:

Классификация индексов

По уникальности

По количеству столбцов

Кластеризованный индекс

Некластеризованный индекс

По способу хранения

Порядок столбцов имеет значение

По структуре данных

Плотный индекс

Таблица индексов имеет структуру (ключ, номер записи в таблице) в отсортированном виде по ключу, при этом она разделена на блоки.

Сбалансированное дерево, как пример индекса РСУБД.

В реляционных СУБД большие таблицы могут содержать тысячи записей, поиск элемента без индекса стоит O(n). Из-за этого используют индексы, сбалансированное дерево является одним из самых популярных индексов

Сбалансированное дерево - структура данных, в которой высота левого и правого поддерева каждого узла отличается не более чем на единицу.

Основные свойства

  1. Высота дерева минимальна
  2. Все листьях находятся примерно на одном уровне
  3. При вставке или удалении структура автоматически перестраивается

Сложность в сбалансированном дереве всегда составляет O(log(n))

Сбалансированные деревья не используются напрямую, в базах данных(postgresql, mysql, oracle) используются B-Tree или B+Tree

Особенности

  1. Каждый узел хранит несколько ключей
  2. Каждый узел может иметь множество потомков
  3. Дерево получается низким( 3-4 уровня для миллиона записей)

B-Tree

Пример B-Tree

Количество узлов выходящих из родителя равно количество узлов родителя + 1

Поиск выполняется следующим образом - B-Tree начинает с верхнего узла и сравнивает значения(сначала меньше/больше, а потом равно ли оно), например возьмем 7. B-Tree начинает слева направо и смотрит:

Пример использования

  1. Пользователь делает поле, например name, индексом

  2. B-Tree записывает в себя отсортированный список имён и распределяет их по дереву

  3. При запросе вида SELECT * FROM users WHERE name='hyilo' происходит следующее

    1. B-Tree смотрит на верхний узел и сравнивает то, что мы хотим найти с записями
    2. До того момента пока B-Tree не найден элемент он будет проходить по узлам

      ВАЖНО! B-Tree оперирует знаками <,>,= это значит что индексироваться могут и строки, и числа

B+Tree работает почти также, как и B-Tree, но у него значения находятся только на листьях, на верхних узлах значений нет - только промежутки

       [30 | 50]
      /    |    \
[10 | 20] [35 | 40] [55 | 60]

Поиск выполняется следующим образом - B+Tree начинает с верхнего узла и сравнивает значения меньше/больше, например возьмем 27. B+Tree начинает слева направо и смотрит:

Сложности

Операция Без индекса С B-Tree
Поиск O(n) O(log n)
Вставка O(n) O(log n)
Удаление O(n) O(log n)

Вставка нового значения

У дерева есть строгие правила по узлам, для разных структур данных оно может иметь разный порядок(m), в большинстве случаях берется m=4

B-Tree порядка m означает, что в одном узле:

Количество детей:

Пример

m Кол-во узлов Кол-во детей
4 1-3 2-4
6 2-5 3-6
8 3-7 4-6

B-Tree

1. Поиск листа

Новое значение всегда вставляется в листовой узел.

  1. Начинает поиск с корня
  2. Сравнивает вставляемый ключ с ключами в узле
  3. По диапазону выбирает нужную ветку
  4. Повторяет, пока не дойдёт до листа

2. Вставка в лист

Пример: Лист [10, 20, 40], вставляем 30[10, 20, 30, 40]

3. Переполнение узла

Если после вставки в узле стало слишком много ключей

  1. Узел делится на два
  2. Средний ключ поднимается в родительский узел
  3. Левые ключи остаются в левом узле, правые — в правом

Пример:

[10 | 20 | 30 | 40 | 50]   ← переполнение

Средний ключ 30 поднимается вверх:

          [30]
         /    \
   [10 | 20]  [40 | 50]

Если родительский узел тоже переполняется после добавления среднего ключа, то операция повторяется рекурсивно вверх.

Если переполняется корень:

B+Tree

Работает практически также

Устройство любого не b-tree индекса РСУБД (полнотекстовый, пространственный, колончатый, и др.).

Колончатые индексы устроены принципиально иначе, чем традиционные строковые индексы (например, B-Tree), и оптимизированы для аналитических запросов, где часто затрагивается лишь небольшое подмножество столбцов таблицы, но при этом обрабатывается большое количество строк.

Ключевой момент: в отличие от обычного индекса, это фактически колоночная организация таблицы, а не отдельная структура поверх таблицы.

Вместо того чтобы хранить строки таблицы подряд, данные разбиваются по столбцам.

Таблица:
id | name  | age
1  | Alice | 30
2  | Bob   | 25
3  | Carl  | 35

Колоночное хранение:
id:   [1, 2, 3]
name: ["Alice", "Bob", "Carl"]
age:  [30, 25, 35]

Блокировка (Chunking) и сегментация

Сжатие

Порядок строк

Оптимизация запросов. Планы запросов.

План запроса

описание того, каким образом СУБД будет выполнять SQL-запрос, включая порядок операций, способы доступа к данным и оценки затрат.

План строится оптимизатором запросов на основе:

План запроса используется для:

Виды планов запросов

По уровню абстракции
Тип плана Описание
Логический Описывает что нужно сделать (SELECT, JOIN, WHERE)
Физический Описывает как именно выполняется запрос
По моменту получения
План Характеристика
Оценочный (Estimated) Строится до выполнения
Фактический (Actual) Получается после выполнения с замерами

Структура плана запроса

План обычно представлен в виде дерева операций, где:

Основные операции плана

Операции доступа к данным
Операция Назначение
Seq Scan Полное сканирование таблицы
Index Scan Чтение через индекс
Index Only Scan Только индекс, без таблицы
Bitmap Index Scan Поиск подходящих строк
Bitmap Heap Scan Чтение строк по битовой карте
Операции соединения таблиц (JOIN)
Тип JOIN Описание
Nested Loop Вложенные циклы
Hash Join Хэш-таблица в памяти
Merge Join Слияние отсортированных данных

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

Операции обработки данных
Операция Функция
Filter Применение условий
Sort Сортировка
Aggregate Агрегация (SUM, COUNT)
GroupAggregate Группировка
HashAggregate Агрегация через хэш
Limit Ограничение строк
Materialize Временное хранение

Метрики плана запроса

Стоимость (Cost)
cost = startup_cost .. total_cost
Метрика Значение
Startup Cost Цена получения первой строки
Total Cost Общая оценка стоимости

Стоимость — условная величина, не время

Количество строк
Метрика Описание
rows (estimated) Ожидаемое число строк
rows (actual) Фактическое число строк

Большая разница → проблемы со статистикой

Время
Показатель Назначение
Planning Time Время построения плана
Execution Time Время выполнения
Использование ресурсов
Метрика Описание
CPU Загрузка процессора
I/O Дисковые операции
Memory Использование памяти
Loops Количество повторений

Оптимизация запросов по плану

Основные методы оптимизации:

Транзакции. ACID.

Транзакция — это набор операций, выполняющихся как единое целое. Обеспечивает целостность данных.

Свойства ACID

  1. Atomicity (Атомарность)
    • Все операции транзакции выполняются полностью или ни одна.
    • Пример: Если одна операция в транзакции не удалась, вся транзакция откатывается.
  2. Consistency (Согласованность)
    • Данные остаются в корректном состоянии после выполнения транзакции.
    • Пример: Если на счету недостаточно средств для перевода, транзакция не выполнится.
  3. Isolation (Изолированность)
    • Транзакции не мешают друг другу, выполняются так, как если бы они шли последовательно.
    • Пример: Если две транзакции пытаются изменить одни и те же данные, они выполняются последовательно.
  4. Durability (Долговечность)
    • Результаты транзакции сохраняются даже после сбоя системы.
    • Пример: После завершения транзакции данные не потеряются, даже если сервер упадет.

Уровни изоляции транзакций. Модели конкурентного доступа.

PostgreSQL поддерживает несколько уровней изоляции, которые определяют, как транзакции видят изменения других транзакций. Уровни изоляции регулируют конкурентный доступ к данным.

Уровни изоляции

  1. Read Uncommitted (Чтение незафиксированных данных)
    • Самый низкий уровень изоляции.
    • Транзакции могут видеть незафиксированные изменения других транзакций.
  2. Read Committed (Чтение зафиксированных данных)
    • Транзакции видят только зафиксированные изменения.
    • Уровень по умолчанию в PostgreSQL.
  3. Repeatable Read (Повторяемое чтение)
    • Гарантирует, что данные, прочитанные в транзакции, не изменятся другими транзакциями до ее завершения.
    • Полезно для предотвращения фантомного чтения.
  4. Serializable (Сериализуемый)
    • Самый строгий уровень изоляции.
    • Гарантирует, что параллельные транзакции выполняются так, как если бы они шли последовательно.
Уровень изоляции Dirty Read Non-Repeatable Read Phantom Read
Read Uncommitted
Read Committed
Repeatable Read
Serializable

Dirty Read (Грязное чтение) — транзакция читает незафиксированные изменения другой транзакции. Если та откатится, данные окажутся неверными.

Non-Repeatable Read (Неповторяемое чтение) — данные, прочитанные транзакцией, могут измениться другой транзакцией до её завершения.

Phantom Read (Фантомное чтение) — при повторном выполнении запроса могут появляться или исчезать новые строки, добавленные или удалённые другими транзакциями.

Пример установки уровня изоляции:

BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- Операции внутри транзакции
SELECT * FROM accounts WHERE user_id = 1;
COMMIT;

Модели конкурентного доступа

  1. Оптимистический (версионированный)
    • Проверка конфликтов после выполнения действия.
    • Пример: пользователь изменяет данные и пытается сохранить изменения. Если версия данных совпадает с текущей — операция проходит, иначе — конфликт, который обрабатывается повторным выполнением или откатом.
  2. Пессимистический (с блокировками)
    • Проверка конфликтов до выполнения действия.
    • Потоки сериализуются на защищаемой области данных (блокируются).
  3. Частично-оптимистический (смешанный)
    • Комбинирует оба подхода: часть операций проверяется заранее, часть — после выполнения.

Блокировки и взаимоблокировки.

Блокировка — запрет другим транзакциям доступа к объекту для предотвращения коллизий и обеспечения целостности данных.

По области действия

По строгости

Intent Lock (Блокировка намерений)

По логике реализации

Двухфазные блокировки (2PL)

  1. Growing (Фаза захвата) — берутся блокировки.
  2. Shrinking (Фаза освобождения) — отпускаются блокировки.

Гарантирует сериализуемость транзакций.

Взаимоблокировки (Deadlock)

Условия:

  1. Mutual Exclusion — ресурс эксклюзивен
  2. Hold and Wait — держит ресурс и ждёт другой
  3. No Preemption — нельзя отобрать ресурс
  4. Circular Wait — замкнутый круг ожиданий

Решения:

Эскалация блокировок

Если слишком много мелких блокировок (строки), СУБД заменяет их на блокировку таблицы для экономии памяти.

Безопасность БД.

Сеть и инфраструктура

Файлы и ОС

Пользователи и доступы

Аудит и логирование

SQL-инъекции

Бэкапы

Прочее

Безопасность БД = сеть + доступы + шифрование + аудит + бэкапы + защита данных + ограничения ресурсов

Архитектура ИС (Клиент-Сервер, Трехзвенная) с точки зрения подключения к СУБД.

Архитектура Клиент-Сервер (2-звенная)

Клиент напрямую обращается к СУБД, формирует SQL и получает данные. Может быть толстый или тонкий клиент. 

Толстый и тонкий клиент
Тип клиента Характер Где логика Обработка запросов к СУБД
Толстый клиент Fat Client Бизнес-логика + UI на клиенте Клиент формирует SQL и напрямую обращается к СУБД
Тонкий клиент Thin Client Логика минимальна Клиент формирует простые SQL-запросы и напрямую обращается к СУБД
Схемы
Толстый клиент (2-звенная)
Пользователь → (логика, запросы SQL) Клиент 
           → (SQL) → Сервер СУБД
                     ← результаты
Тонкий клиент (2-звенная)
Пользователь → UI-только клиент
           → минимальная логика на клиенте
           → SQL → Сервер СУБД
                     ← результат
Общая логика
[ Клиент ]
     |
  SQL запрос
     |
[ Сервер СУБД ]

Трёхзвенная архитектура (3-tier)

Это расширение клиент-серверной модели. Вводится промежуточный уровень приложения (API), который обрабатывает бизнес-логику и взаимодействует с СУБД вместо клиента. Клиент общается только с API.

Схема (3 уровня)
[ Клиент (UI) ]
     |
   API/HTTP
     |
[ Сервер приложений ]
     |
 JDBC/ODBC/ORM
     |
[ Сервер СУБД ]
Уровень Что делает К СУБД подключается?
Клиент Интерфейс пользователя Нет прямого доступа
Сервер приложений / API Бизнес-логика и обработка данных Да
Сервер СУБД Хранит данные и отвечает на запросы

Клиент никогда не общается напрямую с СУБД — только через API сервера приложений. 

Преимущества:

Сравнение архитектур

Характеристика 2-звенная 3-звенная
Кол-во уровней 2 3
К клиенту можно подключиться к БД? Да Нет
Бизнес-логика На клиенте На сервере приложений
Обновление UI Труднее Легче
Масштабируемость Ниже Выше
Безопасность доступа к данным Ниже Выше
СУБД-API Прямой Через сервер приложений