Инженеру данных транзакции нужны не для банковских переводов, а для трёх вещей: чтобы загрузка либо прошла целиком, либо не прошла вовсе; чтобы читатели не видели витрину наполовину обновлённой; чтобы понимать, почему ночной UPDATE завис и ждёт. Этого объёма и держимся.
Атомарность: всё или ничего
BEGIN;
DELETE FROM fact_payment WHERE paid_at >= '2026-06-30' AND paid_at < '2026-07-01';
INSERT INTO fact_payment SELECT ... WHERE paid_at >= '2026-06-30' AND paid_at < '2026-07-01';
COMMIT;Если INSERT упадёт, DELETE откатится вместе с ним, и витрина останется в состоянии до запуска. Без транзакции сбой между двумя командами оставил бы день пустым. Любой шаг загрузки, который состоит из нескольких команд, оборачивается в транзакцию. В Airflow это означает одно соединение и явный COMMIT в конце, а не отдельная задача на каждую команду.
Ошибка внутри транзакции в PostgreSQL переводит её в состояние «только откат»: последующие команды отвергаются до ROLLBACK. Частичный откат делается через точки сохранения: SAVEPOINT s1, затем ROLLBACK TO s1 возвращает к отметке, не отменяя всё.
Что видит читатель: MVCC
PostgreSQL не блокирует чтение записью. Каждая транзакция видит снимок данных на момент своего начала, а изменения других транзакций становятся видны только после их COMMIT. Это называется многоверсионностью, MVCC.
Для пайплайна это означает: пока загрузка внутри транзакции удаляет и вставляет строки, отчёт, запущенный параллельно, спокойно читает старую версию. После COMMIT следующий запрос увидит новую. Переключение мгновенное, промежуточных состояний нет. Поэтому перезапись витрины через TRUNCATE и INSERT в одной транзакции безопасна для читателей: TRUNCATE в PostgreSQL тоже откатывается.
Обратная сторона MVCC: удалённые и обновлённые строки не исчезают физически, а помечаются мёртвыми. Их убирает VACUUM. Таблица, которую каждый день перезаписывают целиком, без вакуума растёт в размере в разы. В модуле Greenplum это отдельный урок про bloat.
Уровни изоляции: что реально нужно знать
По умолчанию PostgreSQL работает в режиме READ COMMITTED: каждая команда внутри транзакции видит всё, что было зафиксировано к моменту её начала. Два SELECT подряд в одной транзакции могут увидеть разные данные, если между ними кто-то сделал COMMIT.
Для отчёта из нескольких запросов, которые должны быть согласованы между собой, есть REPEATABLE READ: снимок делается один раз на транзакцию, и все запросы видят одно состояние. Так собирают сверки, где число строк и сумма считаются разными запросами и обязаны сходиться.
SERIALIZABLE гарантирует, что результат параллельных транзакций эквивалентен какому-то их последовательному выполнению, ценой откатов при конфликте. В аналитических загрузках почти не используется. Грязное чтение, READ UNCOMMITTED, в PostgreSQL не существует вовсе.
Что при этом происходит в других системах: Greenplum наследует модель PostgreSQL. ClickHouse транзакций в привычном смысле не имеет, атомарна вставка одного блока, и идемпотентность там строится на движках таблиц и дедупликации. Spark с Delta или Iceberg даёт атомарную замену партиции через метаданные таблицы.
Блокировки, которые ловит пайплайн
Читатели не мешают писателям, но два писателя мешают друг другу. UPDATE берёт блокировку на изменяемые строки и держит до конца транзакции. Вторая транзакция, которая хочет изменить те же строки, ждёт. Если две загрузки обновляют пересекающиеся наборы строк в разном порядке, возможна взаимная блокировка, deadlock: PostgreSQL обнаружит её и откатит одну из транзакций.
Опаснее блокировки уровня таблицы. ALTER TABLE, TRUNCATE и DROP требуют исключительной блокировки: они ждут завершения всех запросов к таблице и, пока ждут, сами блокируют все новые запросы. Один длинный аналитический SELECT плюс ночная миграция схемы, и очередь встаёт на часы. Правила защиты простые: миграции в окно без нагрузки, lock_timeout на сессию миграции, чтобы она сдалась через минуту, а не держала очередь.
Диагностика: представление pg_stat_activity показывает, какие сессии чего ждут, pg_locks показывает сами блокировки. В Greenplum у долгих транзакций есть ещё одна цена: пока они открыты, VACUUM не может убрать мёртвые строки.
Практика
Песочница этого урока свободная: в ней одно соединение, поэтому параллельные транзакции показать нельзя, но атомарность и откат DDL видны. Выполните стартовый сценарий по шагам, попробуйте убрать ROLLBACK или заменить его на COMMIT и посмотрите на результат последнего запроса. База откатится к исходному состоянию кнопкой «сбросить базу».