OLTP vs OLAP: разные паттерны доступа
До этого модуля курс учил вас строить нормализованные схемы: третья нормальная форма, BCNF, устранение аномалий, целостность через foreign keys. Всё это — правильный инструмент для одной конкретной задачи: обслуживать работающее приложение. Но как только данные нужно не записывать по одной строке, а анализировать миллионами строк сразу, та же самая схема начинает мешать. Не потому, что она «плохая», а потому, что она спроектирована под другую нагрузку.
Этот урок — про две принципиально разные нагрузки на базу данных: OLTP (Online Transaction Processing) и OLAP (Online Analytical Processing). Понимание этой границы — фундамент всего размерного моделирования, которое начинается со следующего модуля. Если вы не чувствуете, чем аналитический запрос отличается от транзакционного на уровне физики, то star schema будет казаться произвольным набором правил. А она не произвольна — она прямое следствие того, как читаются данные в OLAP.
Что такое OLTP-нагрузка
OLTP — это нагрузка работающего приложения. Интернет-магазин принимает заказ, банк проводит платёж, такси-сервис создаёт поездку. Каждая такая операция — короткая транзакция, которая трогает мало строк, но трогает их точечно.
Характерный профиль OLTP-запроса:
- Затрагивает мало строк. Создать заказ — это вставить одну строку в
ordersи несколько вorder_items. Прочитать профиль пользователя — одна строка по primary key. - Точечный доступ по ключу.
WHERE user_id = 4471— поиск по индексу, который мгновенно находит нужную строку. - Смесь чтения и записи. Приложение и читает, и пишет постоянно. Запись — критична.
- Высокая конкурентность. Тысячи пользователей одновременно. Сотни и тысячи транзакций в секунду.
- Низкая латентность важнее всего. Пользователь ждёт ответа. 50 миллисекунд — норма, 2 секунды — провал.
- Свежесть данных абсолютна. Если деньги списались, баланс обязан это отразить немедленно.
OLTP-система оптимизируется под целостность и быструю точечную запись. Нормализованная схема здесь идеальна: каждый факт хранится один раз, обновление одной строки не рискует рассинхронизировать копии, foreign keys гарантируют, что заказ не сошлётся на несуществующего клиента.
Что такое OLAP-нагрузка
OLAP — это нагрузка аналитики. Не «покажи мне заказ 4471», а «покажи выручку по категориям товаров за последние 12 месяцев с разбивкой по регионам». Это другой класс запроса.
Характерный профиль OLAP-запроса:
- Сканирует огромные объёмы. Чтобы посчитать выручку за год, нужно прочитать все строки заказов за год — миллионы или миллиарды.
- Читает мало столбцов из многих. В таблице 40 столбцов, но запросу нужны три: дата, категория, сумма. Остальные 37 — лишний вес.
- Почти только чтение. Аналитик не меняет данные. Запись происходит пакетно, по расписанию (загрузка из источников).
- Низкая конкурентность. Не тысячи пользователей, а десятки аналитиков и дашбордов. Запросов мало, но каждый тяжёлый.
- Throughput важнее латентности. Запрос на 5 секунд — нормально. Запрос, который перемалывает 500 ГБ за минуту — отличная пропускная способность.
- Свежесть терпит задержку. Данные «по состоянию на вчера» обычно достаточно. Лаг в часы приемлем.
OLAP-система оптимизируется под пропускную способность чтения и агрегацию. И здесь нормализованная схема начинает мешать — об этом подробно следующий урок.
Буква O в обеих аббревиатурах — Online. Исторически она противопоставлялась batch-обработке (offline). Сегодня обе нагрузки онлайновые, и буква O несёт мало смысла. Важны буквы TP против AP: Transaction Processing против Analytical Processing.
Откуда берётся противоположность профилей
Стоит на минуту остановиться и спросить: почему профили вообще получились настолько разными? Это не случайность и не вопрос вкуса — различие вытекает из того, кто и зачем обращается к данным.
OLTP обслуживает операционную деятельность. За каждым запросом стоит живой пользователь или процесс, который прямо сейчас совершает действие: жмёт кнопку, оформляет заказ, проводит платёж. Действие касается конкретной сущности — конкретного заказа, конкретного счёта. Отсюда всё остальное: затронуто мало строк (одна сущность — немного строк), доступ точечный (сущность известна по идентификатору), латентность критична (человек ждёт), запись постоянна (действие меняет состояние мира).
OLAP обслуживает принятие решений. За запросом стоит аналитик или дашборд, который хочет понять закономерность: не «что с заказом 4471», а «как ведёт себя выручка целого сегмента». Закономерность не видна на одной строке — её надо извлечь из множества. Отсюда всё остальное: сканируются огромные объёмы (закономерность в массе данных), читается мало столбцов (для метрики важны не все поля, а конкретные), throughput важнее латентности (несколько секунд на тяжёлый расчёт приемлемы), запись пакетная (данные обновляются загрузкой по расписанию, а не действиями пользователей).
То есть оба профиля — не произвольные списки свойств, а связные следствия из одного корневого различия: OLTP работает с отдельными сущностями в моменте, OLAP — с массами данных ради закономерностей. Запомнив этот корень, остальные различия не нужно зубрить — они выводятся.
Один запрос — две судьбы
Возьмём конкретную таблицу заказов и посмотрим, как один и тот же физический объект обслуживает два запроса.
-- Таблица заказов в OLTP-системе магазина
CREATE TABLE orders (
order_id BIGINT PRIMARY KEY,
customer_id BIGINT NOT NULL,
order_date DATE NOT NULL,
status VARCHAR(20),
ship_country VARCHAR(2),
total_amount NUMERIC(12,2),
-- ещё ~30 столбцов: адрес, способ оплаты, промокод, ...
updated_at TIMESTAMP
);
OLTP-запрос — приложение показывает страницу заказа:
SELECT order_id, status, total_amount, ship_country
FROM orders
WHERE order_id = 88123007;
-- order_id | status | total_amount | ship_country
-- 88123007 | shipped | 149.90 | DE
-- (1 row) Время: 0.4 ms
Индекс по order_id приводит ровно к одной строке. База читает одну страницу с диска (или из кеша), отдаёт строку. Молниеносно.
OLAP-запрос — аналитик считает выручку:
SELECT order_date, ship_country, SUM(total_amount) AS revenue
FROM orders
WHERE order_date >= DATE '2025-01-01'
GROUP BY order_date, ship_country
ORDER BY order_date;
-- order_date | ship_country | revenue
-- 2025-01-01 | DE | 184320.50
-- 2025-01-01 | FR | 92110.00
-- ... (тысячи строк)
-- Время: 6.8 s (просканировано 41 млн строк)
Здесь нет точечного доступа. База обязана прочитать каждую строку за год, чтобы её просуммировать. Индекс по order_id бесполезен — фильтр идёт по дате и охватывает миллионы строк. Это full scan по большому диапазону.
| Параметр | OLTP-запрос | OLAP-запрос |
|---|---|---|
| Строк затронуто | 1 | 41 000 000 |
| Столбцов нужно | 4 из ~33 | 3 из ~33 |
| Доступ | точечный по индексу | сканирование диапазона |
| Тип операции | чтение строки | агрегация |
| Целевая метрика | латентность (мс) | пропускная способность |
| Частота | тысячи в секунду | десятки в час |
Обратите внимание: OLAP-запросу нужны 3 столбца, а строка содержит 33. В обычном построчном хранилище база всё равно поднимет с диска все 33 столбца каждой из 41 миллиона строк — потому что строка лежит на диске целиком. 90% поднятых данных будут выброшены. Это и есть корень неэффективности; третий урок модуля разберёт его на уровне физического хранения.
Почему нельзя обслужить обе нагрузки одной системой
Естественный вопрос джуниора: зачем плодить системы? Пусть аналитики ходят прямо в production-базу приложения. Так делают на старте многие команды — и так делать перестают, когда обжигаются. Причин несколько.
Конкуренция за ресурсы. Аналитический запрос, сканирующий 41 миллион строк, занимает CPU, память и дисковый канал на несколько секунд. Пока он работает, OLTP-транзакции тормозят. Один тяжёлый отчёт, запущенный в час пик, способен заметно замедлить оформление заказов. Аналитика и продакшен дерутся за один пул ресурсов.
Блокировки и согласованность чтения. Длинный аналитический запрос видит данные в постоянном движении: пока он считает, приложение меняет строки. В зависимости от СУБД это либо удержание снапшота (раздувание версий строк), либо несогласованный результат.
Противоположные оптимизации. OLTP хочет построчное хранилище, много индексов под точечный доступ, нормализацию. OLAP хочет поколоночное хранилище, сжатие, денормализацию. Невозможно физически разложить одну таблицу так, чтобы она была оптимальна для обоих профилей одновременно — требования прямо противоречат друг другу.
Разный темп изменений. Схема OLTP меняется вслед за фичами приложения. Аналитическая модель должна быть стабильной — на неё завязаны дашборды и отчёты. Смешивать их — значит ронять отчёты каждым релизом приложения.
Поэтому индустрия пришла к разделению: OLTP-система обслуживает приложение, а данные регулярно копируются в отдельную аналитическую систему — data warehouse — со своей моделью, оптимизированной под чтение. Эти две системы связаны процессом перекачки данных (ETL или ELT — четвёртый урок модуля).
OLTP vs OLAP: сравнительный анализ с точки зрения DE Смена ментальных моделей при переходе к ClickHouseHTAP (Hybrid Transactional/Analytical Processing) — класс систем, которые пытаются обслуживать обе нагрузки в одном движке, держа данные сразу в двух представлениях. Они существуют и применяются, но для junior-уровня держите в голове базовую модель: две раздельные системы. Разделение OLTP и OLAP — отправная точка размерного моделирования.
Попробуй сам
Возьмите любое знакомое приложение — маркетплейс, банковское приложение, сервис доставки — и выпишите по три запроса каждого типа.
Для каждого запроса определите: сколько строк он трогает, сколько столбцов ему реально нужно, точечный это доступ или сканирование, что критичнее — латентность или пропускная способность. Например, для маркетплейса OLTP-запрос «добавить товар в корзину» против OLAP-запроса «топ-10 категорий по выручке за квартал».
Затем ответьте на вопрос: если оба класса запросов будут бить в одну базу, какой из них пострадает первым в час пиковой нагрузки и почему? Сформулируйте ответ в терминах конкуренции за CPU и дисковый канал — это и есть инженерное обоснование, зачем нужен отдельный warehouse.