Транзакции и ACID в SQL: шпаргалка
Зачем аналитику знать транзакции
На собесе аналитика это редкий блок, но на middle+ и позициях «DA + DE» спрашивают. Также полезно понимать последствия непоследовательных данных в дашбордах.
ACID — 4 свойства транзакции
Atomicity — атомарность
Транзакция выполняется вся или никак. Если в середине ошибка — rollback.
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;Если второй UPDATE упадёт — первый откатится автоматически.
Consistency — консистентность
После транзакции данные должны быть в валидном состоянии согласно constraints (FK, UNIQUE, CHECK). Если нарушение — rollback.
Isolation — изоляция
Параллельные транзакции не должны мешать друг другу. Уровни — ниже.
Durability — долговечность
После COMMIT данные сохранены (даже при отказе питания). Обеспечивается WAL (Write-Ahead Log).
Синтаксис транзакций
-- Стандарт
BEGIN;
...
COMMIT;
-- Или ROLLBACK в случае ошибки
BEGIN;
...
ROLLBACK;
-- Savepoint — точка возврата внутри транзакции
BEGIN;
INSERT INTO t VALUES (1);
SAVEPOINT s1;
INSERT INTO t VALUES (2);
ROLLBACK TO s1; -- откатываем до s1, но продолжаем транзакцию
COMMIT;Уровни изоляции
От слабого к сильному:
READ UNCOMMITTED
Можно видеть незакоммиченные изменения других транзакций (dirty reads).
Редкий уровень. В PostgreSQL его нет — всегда как READ COMMITTED.
READ COMMITTED (по умолчанию в PostgreSQL)
Видит только закоммиченные данные. Но в одной транзакции при двух чтениях можно получить разные результаты (non-repeatable read).
REPEATABLE READ
Видит snapshot на начало транзакции. Все чтения возвращают тот же результат.
В PostgreSQL реализовано через MVCC.
SERIALIZABLE
Максимум изоляции — как если бы транзакции выполнялись по очереди. Самый медленный.
BEGIN ISOLATION LEVEL SERIALIZABLE;
...
COMMIT;Попробовать силы на подобных вопросах проще всего в тренажёре Карьерник — прямо в Telegram, без регистрации через сайт.
Аномалии многопоточного доступа
Dirty Read
Транзакция A читает незакоммиченные данные B.
Non-Repeatable Read
A читает row X, B его меняет + коммитит, A читает опять — получает другое значение.
Phantom Read
A читает «всех пользователей Москвы» (10 строк), B вставляет нового + коммитит, A читает снова — 11 строк.
Lost Update
A читает значение, B читает его же, оба обновляют — изменения A теряются.
Таблица уровней
| Уровень | Dirty | Non-repeatable | Phantom |
|---|---|---|---|
| READ UNCOMMITTED | возможна | возможна | возможна |
| READ COMMITTED | нет | возможна | возможна |
| REPEATABLE READ | нет | нет | возможна |
| SERIALIZABLE | нет | нет | нет |
Locks (блокировки)
Row-level locks
При UPDATE автоматически блокирует строку до конца транзакции:
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- row id=1 залочена до COMMITSELECT FOR UPDATE
Явная блокировка строк при SELECT:
BEGIN;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
-- теперь никто не может обновить или удалить эту строку
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;Нужно, когда вы читаете, вычисляете, потом обновляете — надо предотвратить параллельные изменения.
Advisory locks
Логические блокировки, не привязанные к строкам:
SELECT pg_advisory_lock(123); -- захватить лок с id=123
-- ... работаем ...
SELECT pg_advisory_unlock(123);Используются для application-level координации.
Deadlock
Когда две транзакции взаимно ждут друг друга:
T1: UPDATE a; UPDATE b;
T2: UPDATE b; UPDATE a;PostgreSQL это детектирует и делает ROLLBACK одной из транзакций. Решения:
- Всегда обновлять в одном порядке.
- Использовать SELECT FOR UPDATE для захвата всего заранее.
Transactional в аналитических запросах
Для SELECT транзакции обычно не используются (нет данных для изменения). Но они полезны для:
- Consistent snapshots для отчёта: открыть REPEATABLE READ транзакцию и делать несколько SELECT с одной точки времени.
- Long-running queries: если нужно, чтобы они видели данные на момент старта.
Пройти 30–50 задач по теме за вечер можно в Telegram-тренажёре. Это то, что отличает «знаю» от «уверенно отвечу на собесе».
10 вопросов с собесов
1. Что такое ACID?
Atomicity, Consistency, Isolation, Durability.
2. Дефолтный уровень в PostgreSQL?
READ COMMITTED.
3. Что такое dirty read?
Чтение незакоммиченных данных. В PostgreSQL невозможно (нет READ UNCOMMITTED).
4. Разница REPEATABLE READ и SERIALIZABLE?
REPEATABLE READ защищает от phantom reads частично (в PostgreSQL через snapshot). SERIALIZABLE — полностью.
5. Когда использовать SELECT FOR UPDATE?
Когда читаете значение, собираетесь его использовать для UPDATE, и не хотите, чтобы другая транзакция его изменила между.
6. Что такое WAL?
Write-Ahead Log — журнал, куда пишутся изменения до их применения. Обеспечивает Durability: после рестарта БД восстанавливается из WAL.
7. Savepoint — зачем?
Частичный rollback внутри транзакции. Если вложенная операция упала — откат только до savepoint, остальное продолжается.
8. Deadlock — как разрешается?
PostgreSQL детектирует через цикл в зависимостях и делает ROLLBACK одной транзакции. Приложение должно retry.
9. MVCC — что это?
Multi-Version Concurrency Control. Каждая транзакция видит snapshot, данные не блокируются при чтении. PostgreSQL использует MVCC вместо lock-based изоляции.
10. Как сделать запрос «вне транзакции»?
В PostgreSQL каждый SELECT без BEGIN — это implicit транзакция. Нельзя «совсем без транзакции». Но можно использовать AUTOCOMMIT и короткие запросы.
Как тренироваться
Транзакции — теория с практикой. Откройте psql, прогоните BEGIN/UPDATE/ROLLBACK несколько раз, почитайте pg_locks во время этого — и всё прояснится.
Тренажёр Карьерник содержит вопросы по транзакциям и изоляции для senior-позиций.
Совет: когда спрашивают ACID — всегда произносите расшифровку. «Atomicity, Consistency, Isolation, Durability — если одно нарушено, это не транзакция».
Читайте также
FAQ
Нужно ли знать ACID аналитику?
На junior — нет. На middle — да, базово. На senior — глубоко, включая уровни изоляции и locks. Для DA/DE hybrid ролей — обязательно.
Когда использовать SERIALIZABLE?
Только когда действительно нужна полная изоляция — денежные переводы, критические операции. Для аналитики почти никогда.
Почему PostgreSQL не имеет READ UNCOMMITTED?
Из-за MVCC архитектуры — все транзакции видят консистентные snapshots, «грязных» данных просто нет в подходящей форме. PostgreSQL маппит READ UNCOMMITTED → READ COMMITTED.
Что такое 2PC (Two-Phase Commit)?
Протокол для распределённых транзакций (несколько БД). В аналитике почти не используется — слишком сложный. Для локальной БД достаточно стандартных транзакций.