транзакция acid
Транзакция ACID — набор из нескольких операций с базой данных, выполняемых как единое неделимое целое, с гарантиями четырёх свойств: Atomicity (атомарность), Consistency (согласованность), Isolation (изолированность) и Durability (долговечность). Аббревиатура ACID введена Теодором Хэрдером и Андреасом Рейтером в 1983 году и по сей день является фундаментом надёжности реляционных СУБД.
Атомарность (Atomicity)
Транзакция — неделимая единица: либо все операции выполняются успешно, либо ни одна из них не применяется к базе данных. Классический пример — банковский перевод: списание со счёта отправителя и зачисление на счёт получателя — это одна транзакция. Если после списания произошёл сбой, откат (ROLLBACK) отменяет списание. Никаких «потерянных» денег.
Реализация атомарности опирается на журнал транзакций (Write-Ahead Log, WAL): все изменения записываются в журнал до применения к данным. При сбое СУБД восстанавливает состояние, применяя или отменяя операции из журнала.
Согласованность (Consistency)
Транзакция переводит базу данных из одного согласованного состояния в другое, не нарушая ни одного ограничения целостности: NOT NULL, UNIQUE, CHECK, FOREIGN KEY, а также бизнес-правил, реализованных через триггеры или приложение. Если транзакция нарушает ограничение, она откатывается целиком.
Важно: согласованность в ACID — это ответственность не только СУБД, но и приложения. СУБД проверяет декларативные ограничения, но бизнес-правила должен обеспечивать код.
Изолированность (Isolation)
Параллельные транзакции изолированы друг от друга — промежуточные состояния одной транзакции не видны другим. Стандарт SQL определяет четыре уровня изоляции, определяющих, какие аномалии допускаются:
- Read Uncommitted — разрешает грязное чтение (читать незафиксированные изменения другой транзакции). На практике почти не используется.
- Read Committed — запрещает грязное чтение. Стандартный уровень в PostgreSQL и Oracle. Одна транзакция видит только зафиксированные данные, но может видеть разные значения при повторных чтениях (non-repeatable read).
- Repeatable Read — гарантирует, что повторное чтение той же строки вернёт то же значение. Защищает от non-repeatable read, но допускает phantom read (появление новых строк). В PostgreSQL Repeatable Read реализован через MVCC и фактически защищает и от phantom read.
- Serializable — транзакции исполняются так, словно они полностью последовательны. Максимальная изоляция, максимальные накладные расходы. PostgreSQL использует SSI (Serializable Snapshot Isolation) — оптимистичную реализацию без полных блокировок.
Долговечность (Durability)
После фиксации (COMMIT) транзакция гарантированно сохранена — даже при немедленном сбое питания или краше сервера. Реализуется через запись в WAL (Write-Ahead Log) на диск до подтверждения COMMIT. Параметр synchronous_commit в PostgreSQL определяет, когда COMMIT считается завершённым: после записи в WAL на диск (по умолчанию) или раньше (асинхронный режим для производительности).
MVCC: многоверсионное управление конкурентностью
PostgreSQL, Oracle и другие СУБД реализуют изоляцию через MVCC (Multiversion Concurrency Control). При обновлении строки СУБД не перезаписывает её, а создаёт новую версию с меткой транзакции. Каждая транзакция видит снимок (snapshot) данных на момент своего начала, игнорируя более новые версии. Это позволяет избежать блокировок при чтении: читатели не блокируют писателей и наоборот.
Практика: использование транзакций в коде
В SQL транзакция обрамляется командами BEGIN ... COMMIT (или ROLLBACK при ошибке). Большинство ORM и библиотек предоставляют высокоуровневые абстракции:
- SQLAlchemy: контекстный менеджер with session.begin()
- Prisma: prisma.$transaction([...]) или интерактивные транзакции
- Django: декоратор @transaction.atomic
- GORM: db.Transaction(func(tx *gorm.DB) error { ... })
Важно: держать транзакции как можно короче. Долгие транзакции удерживают блокировки, накапливают старые версии строк (bloat в PostgreSQL) и ухудшают производительность параллельных запросов.
ACID в NoSQL и NewSQL
Классические NoSQL-базы (MongoDB до версии 4.0, ранний Cassandra) жертвовали ACID-гарантиями ради горизонтального масштабирования. Сегодня MongoDB поддерживает мультидокументные ACID-транзакции, Cassandra реализует легковесные транзакции (LWT), а NewSQL-системы (CockroachDB, YugabyteDB, Google Spanner) предоставляют полные ACID-гарантии при горизонтальном масштабировании через распределённые протоколы консенсуса.
Частые вопросы
Чем отличаются уровни изоляции Read Committed и Repeatable Read?
Read Committed видит только зафиксированные данные, но при повторном чтении одной строки в рамках транзакции может получить разное значение (non-repeatable read). Repeatable Read гарантирует, что повторное чтение той же строки вернёт то же значение — транзакция работает со снимком данных на момент начала. В PostgreSQL Repeatable Read также защищает от phantom reads благодаря MVCC.
Что такое deadlock (взаимная блокировка) и как его избежать?
Deadlock возникает, когда транзакция A ждёт блокировку, удерживаемую транзакцией B, а B ждёт блокировку A — образуется циклическое ожидание. СУБД обнаруживает deadlock и отменяет одну из транзакций. Для профилактики: всегда блокировать ресурсы в одинаковом порядке, держать транзакции короткими, избегать интерактивного ввода внутри транзакции.
Влияет ли уровень изоляции на производительность?
Да. Более высокий уровень изоляции требует больше ресурсов: дополнительные блокировки или версии строк, больше работы планировщика. Read Committed — хороший баланс для большинства приложений. Serializable нужен для критичных финансовых операций. PostgreSQL реализует Serializable через SSI — относительно эффективную оптимистичную технику без полных блокировок таблиц.
Другие термины в теме «Базы данных»
Не хватает деталей?
Напишите, что уточнить по теме «транзакция acid» — это помогает улучшать материал и подсказывает, какие термины добавить дальше. Email необязателен: укажите, если хотите ответ только для вас (мы не шлём рассылки).