Что такое OLTP: короткие транзакции и конкурентность
Нормализацию мы освоили как набор инструментов. Теперь — про контекст, в котором эти инструменты применяются по полной. OLTP (Online Transaction Processing) — это класс систем, ради которых нормализация вообще была придумана. Понять OLTP важно, потому что от паттерна нагрузки напрямую зависит, какой должна быть модель данных.
В этом уроке мы разберём, что такое OLTP, какие у него характерные свойства — короткие транзакции, высокая конкурентность, перекос в сторону записи — и почему именно эти свойства делают нормализованную схему правильным выбором для таких систем.
OLTP — система операций, а не отчётов
OLTP-система обслуживает повседневные операции бизнеса в реальном времени. Это базы данных, стоящие за приложениями, которыми пользуются люди и сервисы прямо сейчас.
Примеры OLTP-нагрузки:
- интернет-магазин: покупатель кладёт товар в корзину, оформляет заказ, оплачивает;
- банковское приложение: перевод денег со счёта на счёт, проверка баланса;
- система бронирования: пользователь бронирует место, отменяет, меняет;
- складская система: товар принят, перемещён, отгружён.
Объединяет их одно: каждое действие — это небольшая, точная операция над небольшим числом строк, и таких операций много, они идут одновременно от множества пользователей. OLTP отвечает на вопрос «какое сейчас состояние данных и как его корректно изменить».
Этому противопоставлен другой класс — OLAP (Online Analytical Processing), аналитические системы: отчёты, дашборды, агрегации по миллионам строк. OLAP отвечает на вопрос «какие закономерности скрыты в накопленных данных». OLTP и OLAP — разные миры с разными требованиями; их сравнению посвящён следующий модуль. Здесь мы целиком про OLTP.
Три характерных свойства OLTP
У OLTP-нагрузки есть три свойства, которые определяют всё остальное — и модель данных, и выбор индексов, и поведение под нагрузкой.
1. Короткие транзакции. Транзакция — это группа операций, выполняемых как единое неделимое целое (подробно — в уроке про ACID). В OLTP транзакции короткие: они затрагивают единицы, реже десятки строк, и завершаются за миллисекунды. «Оформить заказ» — это вставить строку в orders, несколько строк в order_items, обновить остаток товара. Несколько операций, считанные строки, доли секунды.
2. Высокая конкурентность (concurrency). OLTP-базу одновременно дёргают сотни и тысячи клиентов. В один и тот же момент один пользователь оформляет заказ, другой меняет адрес, третий проверяет баланс. Все эти транзакции выполняются параллельно. И иногда они пересекаются — пытаются читать и менять одни и те же строки. Управление таким пересечением — центральная проблема OLTP.
3. Write-heavy: много записи. В аналитике данные в основном читают. В OLTP — постоянно меняют: вставляют новые строки (новый заказ, новый пользователь), обновляют существующие (статус заказа, остаток на складе), удаляют. Доля операций записи (INSERT/UPDATE/DELETE) в OLTP высока — система рассчитана на интенсивный поток изменений.
| Свойство OLTP | Что это значит на практике |
|---|---|
| Короткие транзакции | Единицы-десятки строк за операцию, миллисекунды на транзакцию |
| Высокая конкурентность | Сотни-тысячи параллельных транзакций, пересекающихся по данным |
| Write-heavy | Постоянный поток INSERT/UPDATE/DELETE, не только чтение |
Главная метрика OLTP — это латентность отдельной операции и число транзакций в секунду (throughput по транзакциям). Пользователь, нажавший ‘оформить заказ’, должен получить ответ за десятки миллисекунд. У OLAP метрика другая — время выполнения тяжёлого аналитического запроса. Разные метрики ведут к разным моделям данных.
Анатомия одной OLTP-транзакции
Чтобы три свойства перестали быть абстракцией, разберём одну операцию по шагам. Покупатель нажал «оформить заказ» в интернет-магазине. Что происходит в базе:
BEGIN; -- начало транзакции
INSERT INTO orders (order_id, customer_id, status)
VALUES (5001, 42, 'new'); -- 1 строка
INSERT INTO order_items (order_id, product_id, qty)
VALUES (5001, 'P-7', 2), (5001, 'P-9', 1); -- 2 строки
UPDATE products SET stock = stock - 2
WHERE product_id = 'P-7'; -- 1 строка
UPDATE products SET stock = stock - 1
WHERE product_id = 'P-9'; -- 1 строка
COMMIT; -- зафиксировать как единое целое
Посмотрите на эту транзакцию через три свойства OLTP. Короткая: затронуто пять строк в трёх таблицах, всё выполняется за единицы миллисекунд. Конкурентная: прямо сейчас сотни таких транзакций идут параллельно, и две из них могут одновременно уменьшать stock одного и того же популярного товара — пересечение по строке. Write-heavy: в транзакции четыре операции записи (два INSERT, два UPDATE) и ни одного «чистого» чтения для отчёта — это типичный для OLTP перекос в сторону изменения данных.
И ещё одна важная деталь: пять изменений обёрнуты в BEGIN ... COMMIT. Они обязаны примениться вместе или не примениться вовсе — нельзя, чтобы заказ создался, а остаток товара не уменьшился. Эта неделимость — фундаментальное свойство OLTP-операций; ему посвящён отдельный урок про ACID. Пока зафиксируем: каждая OLTP-операция пользователя — это транзакция, небольшая группа изменений над немногими строками.
Почему OLTP нужна нормализованная схема
Теперь — главное. Три свойства OLTP делают нормализованную схему (3NF, реже BCNF) правильным выбором. Разберём по пунктам.
Write-heavy + конкурентность требуют отсутствия избыточности. Вспомните весь модуль про нормализацию: избыточность порождает update anomaly — один факт надо менять во многих строках. В OLTP, где записи постоянны и конкурентны, это катастрофа. Если город покупателя продублирован в сотне строк заказов, то его смена — это UPDATE сотни строк, который к тому же может пересечься с другой транзакцией. Нормализованная схема хранит каждый факт один раз: смена города — UPDATE одной строки. Меньше строк под изменением — быстрее транзакция, меньше конфликтов конкурентности, нет риска рассинхрона.
Короткие транзакции хорошо ложатся на узкие таблицы. Нормализация даёт много узких таблиц (мало столбцов). Короткая OLTP-транзакция трогает строки в нескольких таких таблицах — и каждое такое обращение точечное, по индексу. Узкая строка означает, что на одну страницу данных помещается больше строк, обновление строки переписывает меньше байт. Нормализованная структура физически дружелюбна к коротким операциям.
Целостность как встроенное свойство. В OLTP корректность данных критична — речь о деньгах, заказах, остатках. Нормализованная схема позволяет выразить правила целостности декларативно: первичные ключи, внешние ключи, ограничения. СУБД сама стережёт эти правила при каждой записи. Чем меньше избыточности, тем больше инвариантов структура гарантирует автоматически, и тем меньше шансов, что параллельные транзакции оставят данные в противоречивом состоянии.
Сравните с аналитикой. Там данные читают тяжёлыми запросами, почти не меняют, и десятки JOIN по нормализованной схеме убивают производительность — поэтому аналитике нужна денормализованная модель. В OLTP наоборот: постоянная конкурентная запись делает нормализацию необходимой. Одна и та же избыточность — дефект в OLTP и инструмент в OLAP. Всё решает паттерн нагрузки.
OLTP: транзакции и точечные операции — обзорный взгляд DEПравило для OLTP: проектируйте схему нормализованной до 3NF. Это минимизирует избыточность, ускоряет конкурентную запись, убирает update-аномалии и позволяет СУБД декларативно стеречь целостность. Денормализация в OLTP — лишь точечное исключение под доказанную проблему чтения, а не отправная точка.
Что дальше в модуле
Раз модель OLTP — нормализованная схема с упором на целостность и конкурентную запись, остальные уроки модуля разбирают, как эту модель довести до рабочего состояния:
- Referential integrity — внешние ключи, которые связывают таблицы и не дают данным «осиротеть».
- Constraints как код модели — CHECK, UNIQUE, NOT NULL: как закодировать бизнес-правила прямо в схеме.
- ACID и конкурентность — как СУБД обеспечивает корректность при параллельных транзакциях и как модель влияет на блокировки.
- Индексы под OLTP — что моделирование должно предусмотреть заранее, чтобы короткие транзакции были быстрыми.
Все эти темы — про одно: как из нормализованной схемы сделать OLTP-базу, которая выдерживает конкурентную нагрузку и гарантирует корректность.
Попробуй сам
Возьмите знакомое приложение — например, сервис заказа такси.
Выполните на бумаге:
- Выпишите 4-5 типичных операций пользователя и водителя (заказать поездку, принять заказ, завершить поездку, оценить и т.п.). Для каждой прикиньте, сколько строк и в скольких таблицах она затрагивает. Убедитесь, что это «короткие транзакции».
- Оцените конкурентность: сколько примерно операций может идти одновременно в час пик и какие из них могут пересечься по одним и тем же строкам (например, два пользователя на одну машину).
- Оцените соотношение чтения и записи: каких операций больше — меняющих данные или только читающих? Это write-heavy нагрузка?
- Сделайте вывод: почему для этого сервиса нормализованная схема подходит лучше, чем одна большая денормализованная таблица. Приведите конкретный пример update anomaly, который возник бы в денормализованном варианте.