Как BEGIN, COMMIT и ROLLBACK объединяют несколько изменений в одну операцию в PostgreSQL
Транзакции в PostgreSQL позволяют группировать SQL-операции в атомарные блоки с помощью команд BEGIN, COMMIT и ROLLBACK. Это обеспечивает целостность данных: либо все изменения применяются, либо ни одно из них не применяется. Механизм основан на принципах ACID - атомарности, согласованности, изоляции и долговечности.
В этом материале
Короткий ответ
Команды BEGIN, COMMIT и ROLLBACK в PostgreSQL управляют блоком транзакции, который объединяет несколько операций в единую логическую единицу. При выполнении BEGIN начинается транзакция; последующие изменения не фиксируются немедленно. Если выполняется COMMIT, все изменения сохраняются навсегда. Если выполняется ROLLBACK, все изменения с момента BEGIN отменяются. Это гарантирует атомарность - либо все шаги успешно завершаются, либо никакие из них не влияют на базу данных.
Введение в транзакции в PostgreSQL
Транзакции являются основой надежных операций с базой данных. Они позволяют группировать несколько SQL-инструкций в единую логическую единицу, которая выполняется как «все или ничего». Это критически важно для поддержания целостности данных, особенно в системах, где ошибки могут привести к неконсистентным состояниям, например, при банковских переводах.
PostgreSQL реализует транзакции в соответствии с принципами ACID: атомарность, согласованность, изоляция и долговечность. Как указано в документации, «транзакция объединяет несколько шагов в один неделимый процесс» - если возникает сбой, никакие промежуточные изменения не влияют на базу данных.
Синтаксис BEGIN, COMMIT и ROLLBACK
Для явного управления транзакцией используются три команды: BEGIN, COMMIT и ROLLBACK. BEGIN запускает блок транзакции. Все последующие операции выполняются в рамках этой транзакции. COMMIT делает все изменения постоянными. ROLLBACK отменяет все изменения, сделанные с момента BEGIN.
Пример: перевод $100 от Алисы Бобу выполняется как одна транзакция:
Приведённый пример перевода предполагает таблицу accounts с уникальным именем и числовым балансом, содержащую существующие строки для Алисы и Боба. Пример с savepoint дополнительно предполагает строку для Уолли. Выполняйте инструкции в контролируемой демонстрационной базе данных. В коде приложения проверяйте количество затронутых строк, достаточность средств и необходимые ограничения; успешный COMMIT сам по себе не доказывает, что произошёл нужный бизнес-перевод.
BEGIN;
UPDATE accounts SET balance = balance - 100.00 WHERE name = 'Alice';
UPDATE accounts SET balance = balance + 100.00 WHERE name = 'Bob';
COMMIT;Атомарность изменений
Атомарность означает, что транзакция либо полностью выполняется, либо полностью откатывается. Промежуточные состояния не сохраняются. Например, если средства были списаны со счёта Алисы, но до зачисления на счёт Боба произошёл сбой, вся транзакция откатывается, и баланс Алисы восстанавливается.
Записи WAL должны быть записаны в устойчивое хранилище до того, как соответствующие страницы данных будут записаны. Страницы данных могут быть записаны до COMMIT; атомарность не означает, что изменения ждут в памяти до коммита. Восстановление после сбоя использует журнал, а правила видимости предотвращают то, чтобы другие сессии видели незакоммиченные изменения в таблицах.
Автоматические транзакции по умолчанию
Вне явного блока транзакции PostgreSQL выполняет каждое SQL-выражение в своей собственной неявной транзакции. Клиентские библиотеки могут автоматически начинать транзакцию или предоставлять опцию autocommit, поэтому перед тем как предполагать, что два выражения независимы, проверьте настройки соединения.
Этот режим удобен для простых операций, но когда необходимо, чтобы несколько команд выполнялись согласованно, требуется явное использование BEGIN и COMMIT, чтобы избежать частичного применения изменений.
Обработка ошибок: откат изменений
Если во время транзакции возникает условие, делающее её недействительной (например, отрицательный баланс), можно выполнить ROLLBACK. Все изменения, сделанные с начала транзакции, будут отменены.
Например, если после списания средств с счёта Алисы её баланс становится отрицательным, транзакцию можно откатить, восстановив первоначальное состояние базы данных.
Видимость изменений для других транзакций
Изменения, сделанные внутри транзакции, не видны другим сессиям до выполнения COMMIT. Это обеспечивает изоляцию. Другие пользователи продолжают видеть предыдущее состояние таблиц.
Только после COMMIT все изменения становятся видимыми одновременно, предотвращая ситуации, когда одна часть операции (например, списание) видна, а другая (например, зачисление) - нет.
Уровни изоляции транзакций
SQL-названия уровней: Read Uncommitted, Read Committed, Repeatable Read и Serializable. PostgreSQL рассматривает Read Uncommitted так же, как Read Committed, поэтому четыре названия дают три различных поведения. Read Committed - стандартный уровень по умолчанию; его можно изменить через конфигурацию сессии или базы данных.
Read Committed позволяет видеть только данные, закоммиченные до начала запроса. Repeatable Read предоставляет согласованный снимок на момент начала транзакции. Serializable имитирует последовательное выполнение, но может требовать обработки ошибок сериализации.
Использование точек сохранения для частичного отката
SAVEPOINT позволяет создать точку внутри транзакции, до которой можно откатиться без отмены всей транзакции. Это полезно в сложной логике, когда нужно отменить только часть операции.
Например, если после перевода денег Бобу обнаруживается, что деньги должны были получить Уолли, транзакцию можно откатить до точки сохранения и перенаправить средства.
BEGIN;
UPDATE accounts SET balance = balance - 100.00 WHERE name = 'Alice';
SAVEPOINT my_savepoint;
UPDATE accounts SET balance = balance + 100.00 WHERE name = 'Bob';
ROLLBACK TO my_savepoint;
UPDATE accounts SET balance = balance + 100.00 WHERE name = 'Wally';
COMMIT;Что проверить
- Проверьте настройки транзакции клиента и автокоммита перед выполнением BEGIN.
- Проверяйте балансы и количество затронутых строк; атомарность сама по себе не гарантирует корректность бизнес-логики.
- После ошибки в инструкции выполните ROLLBACK перед повторным использованием соединения.
- Держите транзакции короткими и повторяйте попытки при ошибках сериализации в соответствии с политикой приложения.
Границы применения
Изменения в последовательностях (sequences) не откатываются при ROLLBACK и сразу становятся видимыми для других транзакций. Более высокие уровни изоляции могут вызывать ошибки сериализации, требующие повторного выполнения транзакции.