Какую функцию выполняет транзакция
Что проверяют
Понимаете ли вы, зачем транзакция нужна, кроме «чтобы откатить». Формулировка простая, но за ней идут уровни изоляции, аномалии и работа с транзакцией из кода. Частое продолжение: «какой уровень по умолчанию в вашей базе и от чего не защищает».
Как отвечать
Определение через границу: транзакция это набор операций, который для остальных выглядит как одно действие: применились либо все изменения, либо ни одного. Дальше ACID, но с пояснениями, а не расшифровкой букв:
- атомарность: откат по ошибке или по обрыву клиента, в PostgreSQL за это отвечают MVCC и WAL;
- согласованность: база остаётся в рамках ограничений (внешние ключи,
CHECK, уникальность), часть проверок откладывается до коммита черезDEFERRABLE; - изолированность: главное в ответе. Read Committed видит чужие коммиты между своими запросами, Repeatable Read работает на снимке, Serializable даёт результат, эквивалентный последовательному выполнению;
- устойчивость: после коммита изменения переживают падение, за это отвечает сброс журнала на диск (
synchronous_commit).
Назовите аномалии: грязное чтение, неповторяющееся чтение, фантомы, потерянное обновление, перекос записи (write skew). В PostgreSQL по умолчанию Read Committed, грязного чтения нет ни на одном уровне, Repeatable Read это снимок и от перекоса не спасает, а Serializable спасает ценой ошибки сериализации (40001), и её нужно повторить. В MySQL InnoDB по умолчанию Repeatable Read с блокировкой промежутков.
Про Go практика: db.BeginTx(ctx, ...), defer tx.Rollback() (после Commit он безвреден и вернёт ErrTxDone), никаких походов по сети внутри транзакции, сами транзакции короткие, иначе выест пул соединений.
Где спотыкаются
Пересказывают ACID четырьмя словами и не могут привести пример аномалии.
Считают, что транзакция сама защищает от гонки. Два параллельных «прочитал остаток, вычел, записал» на Read Committed потеряют одно списание. Нужен SELECT ... FOR UPDATE, изменение прямо в выражении UPDATE или уровень выше с повтором.
Держат транзакцию открытой на время внешнего запроса или ожидания пользователя. Долгие транзакции мешают очистке старых версий строк и раздувают таблицу.