Основы
7 мин чтения·

Что такое ACID: атомарность, согласованность, изолированность, долговечность

ACID — это четыре свойства, которые СУБД гарантирует при выполнении транзакций: Atomicity (атомарность), Consistency (согласованность), Isolation (изолированность) и Durability (долговечность). Вместе они означают одно: данным можно доверять, даже если сервер упал на середине операции, а к базе одновременно обращаются тысячи клиентов.

Сама транзакция — это группа SQL-операций, которая выполняется как единое целое: либо все операции успешно завершатся, либо не выполнится ни одна. Классический пример — перевод денег:

MySQL 8.1
START TRANSACTION;

UPDATE accounts SET balance = balance - 1000 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 1000 WHERE account_id = 2;

COMMIT;

Между двумя UPDATE может случиться что угодно: отключение электричества, обрыв сети, падение процесса СУБД. Если первая команда выполнится, а вторая нет — тысяча рублей просто исчезнет. Свойства ACID существуют ровно для того, чтобы такие истории были невозможны. Разберём каждое.

Четыре свойства ACID

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

Транзакция неделима: выполняются либо все её операции, либо ни одной.

В примере с переводом атомарность гарантирует: не существует состояния базы, в котором деньги списаны с одного счёта, но не зачислены на другой. Если на любом шаге что-то пошло не так, СУБД выполняет откат — возвращает данные к состоянию на момент начала транзакции, как будто её и не было.

Откат можно вызвать и вручную командой ROLLBACK:

MySQL 8.1
START TRANSACTION;

UPDATE accounts SET balance = balance - 1000 WHERE account_id = 1;
-- передумали: отменяем все изменения этой транзакции
ROLLBACK;

Под капотом СУБД ведёт журнал изменений: прежде чем менять данные, она записывает, что собирается сделать и как это отменить. После сбоя база восстанавливается по журналу: завершённые транзакции доводятся до конца, незавершённые откатываются.

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

Транзакция переводит базу из одного корректного состояния в другое корректное.

«Корректность» задают правила, описанные в схеме: ограничения NOT NULL, UNIQUE, CHECK, внешние ключи, триггеры. Если внутри транзакции какое-то правило нарушено — например, баланс стал отрицательным при ограничении CHECK (balance >= 0) — транзакция откатится целиком.

Важный нюанс: СУБД проверяет только те правила, которые вы ей описали. Если правило «сумма списаний равна сумме зачислений» существует лишь в голове разработчика, база его не защитит. Согласованность — совместная работа СУБД и проектировщика схемы.

I — Isolation, изолированность

Параллельные транзакции не видят промежуточных состояний друг друга.

Пока один клиент переводит деньги, другой в этот же момент считает сумму по всем счетам. Без изоляции второй клиент мог бы увидеть базу «на полпути»: деньги уже списаны, но ещё не зачислены — итоговая сумма сойдётся с ошибкой в тысячу рублей.

В идеале транзакции должны вести себя так, будто выполняются строго по очереди. Но полная изоляция дорога: транзакциям приходится ждать друг друга. Поэтому СУБД предлагают компромиссы — уровни изоляции. Чем уровень ниже, тем быстрее работает база и тем больше странностей могут наблюдать параллельные транзакции. Эти странности называют аномалиями чтения:

  • Грязное чтение (dirty read). Транзакция видит изменения, которые другая транзакция ещё не зафиксировала. Если та откатится — прочитанных данных никогда «официально» не существовало.
  • Неповторяющееся чтение (non-repeatable read). Транзакция читает одну и ту же строку дважды и получает разные значения, потому что между чтениями другая транзакция изменила строку и зафиксировалась.
  • Фантомное чтение (phantom read). Транзакция выполняет один и тот же запрос с условием дважды и во второй раз видит новые строки, которые успела добавить другая транзакция.

Как выглядит аномалия вживую, проще всего показать на таймлайне двух параллельных сессий. Вот неповторяющееся чтение на уровне READ COMMITTED:

ШагСессия AСессия B
1BEGIN;
2SELECT balance ...100
3UPDATE ... SET balance = 50; COMMIT;
4SELECT balance ...50
5Один и тот же запрос — два разных ответа

Сессия A не делала ничего между шагами 2 и 4, но мир под ней поменялся. На уровне REPEATABLE READ шаг 4 вернул бы те же 100: транзакция работает со снимком данных на момент своего начала.

Стандарт SQL описывает четыре уровня изоляции — каждый следующий запрещает больше аномалий:

Уровень изоляцииГрязное чтениеНеповторяющееся чтениеФантомное чтение
READ UNCOMMITTEDвозможновозможновозможно
READ COMMITTEDнетвозможновозможно
REPEATABLE READнетнетвозможно
SERIALIZABLEнетнетнет

По умолчанию PostgreSQL работает на уровне READ COMMITTED, а MySQL (InnoDB) — на REPEATABLE READ. На практике этого почти всегда достаточно, но знать, какие аномалии возможны на вашем уровне, полезно: «плавающие» баги в отчётах и счётчиках часто растут именно отсюда.

Уровень можно поменять для конкретной транзакции:

MySQL 8.1
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
-- критичная к точности логика
COMMIT;

Транзакция дважды выполнила запрос SELECT COUNT(*) FROM orders WHERE status = 'new' и получила разные числа: между запросами другая транзакция добавила заказы. Как называется эта аномалия?

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

Если транзакция зафиксирована, её результат не потеряется.

Как только СУБД ответила «COMMIT выполнен», изменения переживут что угодно: аварийное выключение сервера, падение процесса, перезагрузку. Достигается это тем, что перед подтверждением запись о транзакции принудительно сбрасывается на диск в журнал (в PostgreSQL он называется WAL — write-ahead log, в MySQL InnoDB — redo log). Даже если данные в таблицах обновиться не успели, после рестарта база доведёт их до нужного состояния по журналу.

Сервер базы данных аварийно перезагрузился в момент, когда транзакция выполнила первый из двух UPDATE. Что произойдёт с этим изменением после рестарта?

ACID vs BASE: а как же NoSQL

В мире распределённых NoSQL-хранилищ часто используется другой набор компромиссов — BASE: Basically Available, Soft state, Eventually consistent. Его суть — система всегда отвечает, но данные на разных узлах могут какое-то время расходиться и сойтись «в конечном счёте».

Это не «хуже» и не «лучше» ACID — это другой выбор. Платёжной системе нужна строгость ACID: потерянный перевод недопустим. Счётчику лайков подойдёт BASE: ничего страшного, если число на секунду отстанет. Многие современные системы комбинируют оба подхода для разных частей данных.

Что спрашивают на собеседованиях

Тема ACID — одна из самых частых на интервью по SQL и базам данных. Типичные вопросы:

  • Расшифруйте ACID и объясните каждое свойство. Достаточно примера с переводом денег — он покрывает все четыре буквы.
  • Чем атомарность отличается от согласованности? Атомарность — про «всё или ничего», согласованность — про соблюдение правил схемы в итоговом состоянии.
  • Какие уровни изоляции вы знаете и какой используется по умолчанию? Здесь ждут таблицу выше + значения по умолчанию вашей СУБД.
  • Что такое грязное чтение и на каком уровне оно возможно? Только на READ UNCOMMITTED.
  • Как база гарантирует долговечность, если данные кэшируются в памяти? Ключевое слово — журнал упреждающей записи (WAL/redo log): на диск до подтверждения попадает запись в журнале, а не сами страницы данных.

Что дальше

ACID — это теория, которую стоит сразу закрепить практикой:

Читайте по теме