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

Транзакция объединяет операции в одну логическую единицу. Она заканчивается COMMIT или ROLLBACK; при ошибке внутри транзакции приложение должно знать, можно ли продолжать, откатывать до savepoint или откатывать все.

BEGIN;
 
UPDATE orders
SET state = 'PAID'
WHERE id = 42
  AND state = 'NEW';
 
INSERT INTO payment_events (order_id, event_type)
VALUES (42, 'ORDER_PAID');
 
COMMIT;

Проверка старого состояния в UPDATE важна: транзакция атомарна, но это не делает бизнес-переход корректным автоматически.

Atomicity — атомарность

Операции транзакции либо фиксируются вместе, либо их эффект отменяется. Если процесс или СУБД падает, восстановление не должно оставлять в зафиксированном состоянии только половину транзакции.

Атомарность сама по себе не определяет, что параллельная транзакция может увидеть до commit. Это задача изоляции: например, уровень READ UNCOMMITTED может разрешить наблюдение незавершённых изменений. Атомарность обеспечивается механизмами журнала, отмены/версий и восстановления конкретной СУБД.

Она не распространяется автоматически на HTTP-вызов, Kafka и файловую систему. Для согласования БД с внешним миром применяют outbox, идемпотентность и компенсации.

Consistency — согласованность

Транзакция переводит БД из одного допустимого состояния в другое. Часть правил обеспечивает база (NOT NULL, CHECK, UNIQUE, FOREIGN KEY), часть остается в доменной логике.

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

Isolation — изоляция

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

Изоляция — не обязательно глобальная очередь. СУБД используют MVCC, блокировки, проверку конфликтов и сериализацию. Подробнее: Изоляция транзакций.

Durability — долговечность

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

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

Граница транзакции

Граница должна охватывать одну бизнес-операцию, но не удерживать ресурсы дольше необходимого:

  • не делать медленный сетевой вызов под блокировкой без явной причины;
  • не отдавать наружу успех до гарантии нужного commit;
  • учитывать rollback-only и правила отката фреймворка;
  • повторять транзакцию только если операция идемпотентна и ошибка допускает retry.

Связано: Ограничения, constraints, Lost Update, Опасности использования read committed, Transactional.

Вопросы

Обеспечивает ли транзакция согласованность между БД и Kafka? Нет. Нужен отдельный протокол, например transactional outbox.

Означает ли serializable последовательное физическое выполнение? Нет. Результат должен быть эквивалентен некоторому последовательному порядку; транзакции могут работать параллельно и конфликтовать при commit.

Может ли commit завершиться, а клиент получить ошибку? Да, например при потере соединения после фиксации. Поэтому повтор команды должен учитывать неопределенный исход.

Кто отвечает за Consistency? И ограничения БД, и корректная бизнес-логика.

Всегда ли durability равна «данные никогда не потеряются»? Нет, гарантия ограничена моделью отказов и настройками хранения/репликации.