Урок по треку SQL и базы данных 8 мин

Транзакции и изоляция за 8 минут

ACID, аномалии, уровни изоляции, MVCC и блокировки в PostgreSQL.

Транзакция это группа операций, которая выполняется как одно целое.

BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;   -- или ROLLBACK, если что-то пошло не так

ACID

  • Atomicity (атомарность): либо все изменения, либо ни одного.
  • Consistency (согласованность): после транзакции соблюдены все ограничения (ключи, CHECK, внешние ключи).
  • Isolation (изоляция): параллельные транзакции не мешают друг другу сильнее, чем позволяет уровень изоляции.
  • Durability (долговечность): после COMMIT данные переживут падение сервера. В PostgreSQL это обеспечивает WAL (журнал упреждающей записи): изменение сначала пишется в журнал на диск.

Аномалии параллельного доступа

  • Dirty read: читаем незакоммиченные изменения другой транзакции.
  • Non-repeatable read: читаем строку дважды, между чтениями её изменили и закоммитили, видим разное.
  • Phantom read: повторный запрос с тем же условием возвращает новые строки, которые вставила другая транзакция.
  • Lost update: две транзакции прочитали значение, обе записали своё, одно изменение потерялось.
  • Write skew: две транзакции читают пересекающиеся данные и меняют разные строки, вместе нарушая инвариант (оба дежурных врача снялись с дежурства, каждый видел, что второй остаётся).

Уровни изоляции по стандарту

УровеньDirty readNon-repeatablePhantom
Read Uncommittedвозможновозможновозможно
Read Committedнетвозможновозможно
Repeatable Readнетнетвозможно
Serializableнетнетнет

Как это в PostgreSQL

  • Уровень по умолчанию: Read Committed. Каждый оператор видит снимок данных на момент своего начала.
  • Read Uncommitted ведёт себя как Read Committed: грязного чтения в PostgreSQL нет вообще.
  • Repeatable Read работает как snapshot isolation: снимок берётся один на всю транзакцию, поэтому фантомов тоже нет. Если пытаемся изменить строку, которую после нашего снимка изменила другая закоммиченная транзакция, получаем ошибку сериализации, транзакцию надо повторить.
  • Serializable (SSI) дополнительно ловит write skew и откатывает одну из конфликтующих транзакций. Приложение обязано уметь повторять транзакцию.

MVCC

PostgreSQL не перезаписывает строку при UPDATE: он создаёт новую версию, а старую помечает удалённой. Каждая транзакция видит версии, подходящие её снимку. Поэтому читатели не блокируют писателей, а писатели читателей. Цена: мёртвые версии копятся, их убирает VACUUM (обычно autovacuum). Долгая открытая транзакция мешает VACUUM чистить таблицы и раздувает их.

Блокировки

Две транзакции, меняющие одну строку, всё равно конфликтуют: вторая ждёт, пока первая завершится.

  • SELECT ... FOR UPDATE блокирует выбранные строки до конца транзакции: классический способ избежать lost update (пессимистичная блокировка).
  • FOR UPDATE SKIP LOCKED пропускает занятые строки: так делают очереди задач на таблице.
  • Оптимистичная блокировка: колонка version, UPDATE ... SET version = version + 1 WHERE id = 1 AND version = 7, если затронуто 0 строк, кто-то успел раньше.
  • Атомарный UPDATE SET balance = balance - 100 безопаснее, чем «прочитал в приложении, посчитал, записал».

Deadlock: транзакция A держит строку 1 и ждёт строку 2, B держит 2 и ждёт 1. PostgreSQL обнаруживает это и откатывает одну из них. Профилактика: брать блокировки в одном порядке (например, по возрастанию id).

Частые ошибки

  • Читать в приложении, считать и записывать без блокировки или версии.
  • Держать транзакцию открытой во время HTTP-запроса во внешний сервис.
  • Включить Serializable и не написать повтор транзакции.
  • Думать, что Read Uncommitted в PostgreSQL покажет грязные данные.

Что спросят на собесе

  • Расшифруй ACID.
  • Какие бывают аномалии и какие уровни изоляции от чего защищают.
  • Какой уровень по умолчанию в PostgreSQL и как работает MVCC.
  • Как избежать lost update при списании баланса.
  • Что такое deadlock и как его избежать.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.