Транзакции являются фундаментальной концепцией всех систем баз данных. Важным моментом транзакции является то, что она объединяет несколько шагов в единую операцию «все или ничего». Промежуточные состояния между шагами не видны другим параллельным транзакциям, и если происходит какой-то сбой, препятствующий завершению транзакции, то ни один из шагов вообще не влияет на базу данных.

Например, рассмотрим базу данных банка, которая содержит остатки по различным счетам клиентов, а также общие остатки по депозитам в филиалах. Предположим, что мы хотим записать платеж в размере 100 долларов со счета Алисы на счет Боба. Возмутительно упрощая, SQL-команды для этого могут выглядеть так:

UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
UPDATE branches SET balance = balance - 100.00
    WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Alice');
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Bob';
UPDATE branches SET balance = balance + 100.00
    WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Bob');

Детали этих команд здесь не важны; важно то, что для выполнения этой довольно простой операции требуется несколько отдельных обновлений. Сотрудники нашего банка захотят быть уверенными, что либо все эти обновления происходят, либо ни одно из них. Конечно, сбой системы не может привести к тому, что Боб получит 100 долларов, которые не были списаны с Алисы. Алиса также не смогла бы долго оставаться довольным клиентом, если бы с нее списывались деньги, а Боб не кредитовался. Нам нужна гарантия того, что если что-то пойдет не так во время выполнения операции, ни один из выполненных до сих пор шагов не вступит в силу. Группировка обновлений в транзакцию дает нам эту гарантию. Говорят, что транзакция является атомарной .: с точки зрения других транзакций это либо происходит полностью, либо не происходит вообще.

Нам также нужна гарантия того, что после того, как транзакция будет завершена и подтверждена системой базы данных, она действительно будет постоянно записана и не будет потеряна, даже если вскоре после этого произойдет сбой. Например, если мы записываем снятие наличных Бобом, мы не хотим, чтобы списание средств с его счета исчезло в результате аварии сразу после того, как он выйдет за дверь банка. Транзакционная база данных гарантирует, что все обновления, сделанные транзакцией, регистрируются в постоянном хранилище (т. е. на диске) до того, как транзакция будет завершена.

Еще одно важное свойство транзакционных баз данных тесно связано с понятием атомарных обновлений: когда несколько транзакций выполняются одновременно, каждая из них не должна видеть незавершенные изменения, сделанные другими. Например, если одна транзакция занята подведением итогов всех балансов филиалов, для нее не годится включать дебет филиала Алисы, но не кредит филиала Боба, и наоборот. Таким образом, транзакции должны быть по принципу «все или ничего» не только с точки зрения их постоянного воздействия на базу данных, но и с точки зрения их видимости по мере их совершения. Обновления, сделанные открытой транзакцией, невидимы для других транзакций, пока транзакция не завершится, после чего все обновления становятся видимыми одновременно.

В PostgreSQL транзакция создается путем окружения SQL-команд транзакции командами BEGINи COMMIT. Таким образом, наша банковская транзакция будет выглядеть так:

BEGIN;
UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
-- etc etc
COMMIT;

Если в процессе транзакции мы решим, что не хотим совершать коммит (возможно, мы только что заметили, что баланс Алисы стал отрицательным), мы можем ввести команду ROLLBACKвместо COMMIT, и все наши обновления на данный момент будут отменены.

PostgreSQL фактически рассматривает каждый оператор SQL как выполняемый в рамках транзакции. Если вы не выдаете BEGINкоманду, то каждый отдельный оператор имеет неявный BEGINи (в случае успеха) COMMITобернутый вокруг него. Группа операторов, окруженная BEGINи COMMITиногда называемая блоком транзакции .

Примечание

Некоторые клиентские библиотеки выдают BEGINи COMMITвыполняют команды автоматически, так что вы можете получить эффект блокировки транзакций, не спрашивая. Проверьте документацию по используемому интерфейсу.

Операторы в транзакции можно контролировать более детально, используя точки сохранения . Точки сохранения позволяют вам выборочно отбрасывать части транзакции, сохраняя при этом остальные. После определения точки сохранения с помощью SAVEPOINT, вы можете при необходимости вернуться к точке сохранения с помощью ROLLBACK TO. Все изменения базы данных транзакции между определением точки сохранения и откатом к ней отбрасываются, но изменения до точки сохранения сохраняются.

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

Все это происходит внутри блока транзакций, поэтому ничего из этого не видно другим сеансам базы данных. Когда и если вы фиксируете блок транзакций, зафиксированные действия становятся видимыми как единое целое для других сеансов, в то время как отмененные действия вообще никогда не становятся видимыми.

Вспоминая банковскую базу данных, предположим, что мы дебетуем 100 долларов со счета Алисы и кредитуем счет Боба, но позже обнаруживаем, что должны были кредитовать счет Уолли. Мы могли бы сделать это, используя такие точки сохранения:

BEGIN;
UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
SAVEPOINT my_savepoint;
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Bob';
-- oops ... forget that and use Wally's account
ROLLBACK TO my_savepoint;
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Wally';
COMMIT;

Этот пример, конечно, слишком упрощен, но в блоке транзакций возможен большой контроль за счет использования точек сохранения. Более того, ROLLBACK TOэто единственный способ восстановить контроль над блоком транзакций, который был переведен системой в прерванное состояние из-за ошибки, за исключением полного отката и запуска заново.