Kodokon kodokon.com

Транзакции: BEGIN, COMMIT, ROLLBACK и ACID

Сделай записи атомарными с помощью транзакций и пойми, что гарантии ACID означают в повседневной практике.

8 мин · 3 вопросов

Открыть этот урок в Kodokon

Банковский перевод - это два UPDATE: списать с одного счёта, зачислить на другой. Если второй упадёт, база данных начнёт врать. Транзакция делает такой блок неделимым, с гарантиями ACID: Atomicity, атомарность (всё или ничего), Consistency, согласованность (ограничения остаются в силе), Isolation, изоляция (параллельные транзакции никогда не видят друг друга наполовину выполненными), Durability, долговечность (COMMIT переживает сбой). Для этого урока лучше взять настоящий клиент sqlite3: некоторые веб-песочницы автоматически коммитят каждый запрос.

SQL
CREATE TABLE accounts (
  id INTEGER PRIMARY KEY,
  owner TEXT NOT NULL,
  balance REAL NOT NULL CHECK (balance >= 0)
);

INSERT INTO accounts (owner, balance)
VALUES
  ('alice', 500.0),
  ('bruno', 120.0);
Ограничение CHECK запрещает любой отрицательный баланс.

Штатный случай: BEGIN открывает транзакцию, записи накапливаются, COMMIT делает их окончательными разом. Между этими двумя моментами ни одно другое соединение не видит промежуточное состояние (у alice уже списано, а bruno ещё не зачислено).

SQL
BEGIN;

UPDATE accounts
SET balance = balance - 200
WHERE owner = 'alice';

UPDATE accounts
SET balance = balance + 200
WHERE owner = 'bruno';

COMMIT;

SELECT owner, balance FROM accounts;
Атомарный перевод: 300 у alice, 320 у bruno.

Теперь случай сбоя. Списание 400 у bruno нарушило бы CHECK (balance >= 0): инструкция падает. Тонкость SQLite: по умолчанию ошибка отменяет проблемную инструкцию, но оставляет транзакцию открытой. Решать твоему коду приложения: ROLLBACK, чтобы откатить всё (здравый рефлекс), или продолжить, если ошибка поправима.

SQL
BEGIN;

UPDATE accounts
SET balance = balance - 400
WHERE owner = 'bruno';
-- Error: CHECK (balance >= 0) rejects the row.

ROLLBACK;

SELECT owner, balance FROM accounts;
После ROLLBACK балансы на момент последнего COMMIT остаются целы.

Проверка знаний

Убедись, что запомнил ключевые моменты этого урока.

  1. Что гарантирует атомарность, буква A в ACID?
    • Запросы транзакции выполняются быстрее
    • Транзакция применяется целиком или не применяется вовсе
    • Две транзакции никогда не могут читать одну и ту же таблицу
  2. В каком состоянии окажется база данных после ROLLBACK?
    • В состоянии последнего COMMIT, как будто BEGIN и не было
    • UPDATE, которые прошли до ошибки, сохраняются
    • База остаётся заблокированной до следующего BEGIN
  3. Почему 10 000 INSERT намного быстрее в одной транзакции SQLite?
    • SQLite сжимает вставляемые данные пакетом
    • Одна синхронизация диска на COMMIT вместо одной на каждую инструкцию
    • Индексы отключаются на время транзакции