Управление транзакциями в PostgreSQL

PostgreSQLBeginner
Практиковаться сейчас

Введение

В этом лабораторном практикуме вы изучите управление транзакциями в PostgreSQL, критически важном аспекте обеспечения целостности данных. Вы узнаете, как начинать и завершать транзакции, рассматривая серию операций как единый блок работы. Вы также научитесь откатывать неудачные транзакции, устанавливать уровни изоляции и моделировать блокировки при одновременных обновлениях.

Начало и фиксация транзакции

В этом шаге вы узнаете, как начать и завершить транзакцию в PostgreSQL. Транзакции обеспечивают целостность данных, рассматривая серию операций как единый блок работы. Если какая-либо операция внутри транзакции завершается ошибкой, вся транзакция отменяется, предотвращая частичные обновления и сохраняя согласованность.

Сначала подключитесь к базе данных PostgreSQL как пользователь postgres. Откройте терминал и используйте следующую команду:

sudo -u postgres psql

Теперь вы должны увидеть приглашение postgres=#.

Далее создайте таблицу с именем accounts, чтобы продемонстрировать транзакции.

CREATE TABLE accounts (
    id SERIAL PRIMARY KEY,
    name VARCHAR(50),
    balance DECIMAL(10, 2)
);

Вставьте начальные данные в таблицу accounts.

INSERT INTO accounts (name, balance) VALUES ('Alice', 100.00);
INSERT INTO accounts (name, balance) VALUES ('Bob', 50.00);

Теперь начнем транзакцию с помощью команды BEGIN.

BEGIN;

В рамках транзакции переведем 20 долларов с счета Алисы на счет Боба.

UPDATE accounts SET balance = balance - 20.00 WHERE name = 'Alice';
UPDATE accounts SET balance = balance + 20.00 WHERE name = 'Bob';

Чтобы сделать эти изменения постоянными, завершите транзакцию с помощью команды COMMIT.

COMMIT;

Проверьте успешность транзакции, запросив таблицу accounts.

SELECT * FROM accounts;

Вы должны увидеть, что баланс Алисы уменьшился на 20 долларов, а баланс Боба увеличился на 20 долларов.

Пример вывода транзакции PostgreSQL

Наконец, выйдите из оболочки psql.

\q

Откат неудачной транзакции

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

Вы должны быть подключены к базе данных PostgreSQL из предыдущего шага. Если нет, подключитесь снова, используя:

sudo -u postgres psql

Начнем новую транзакцию.

BEGIN;

В рамках этой транзакции мы попытаемся выполнить операцию, которая преднамеренно завершится ошибкой. Попробуем вставить дублирующий первичный ключ. Сначала найдем следующее доступное значение id.

SELECT MAX(id) FROM accounts;

Предположим, результат равен 2. Теперь попробуем вставить новую запись с id = 1, который уже существует.

INSERT INTO accounts (id, name, balance) VALUES (1, 'Eve', 25.00);

Эта команда приведет к ошибке ERROR: duplicate key value violates unique constraint "accounts_pkey".

Поскольку в транзакции произошла ошибка, откатим транзакцию, чтобы отбросить все внесенные изменения. Используйте команду ROLLBACK.

ROLLBACK;

Проверьте успешность отката, запросив таблицу accounts.

SELECT * FROM accounts;

Вы должны увидеть, что таблица по-прежнему содержит только счета Алисы и Боба с их балансами из конца шага 1. Неудачная операция INSERT была успешно откатана.

Пример отката транзакции PostgreSQL

Наконец, выйдите из оболочки psql.

\q

Установка и проверка уровней изоляции

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

Вы должны быть подключены к базе данных PostgreSQL из предыдущего шага. Если нет, подключитесь снова, используя:

sudo -u postgres psql

Откройте два отдельных терминала. В каждом терминале подключитесь к базе данных PostgreSQL как пользователь postgres. У вас должно быть два приглашения postgres=#.

Терминал 1:

sudo -u postgres psql

Терминал 2:

sudo -u postgres psql

В Терминале 1 установите уровень изоляции на READ COMMITTED (хотя это значение по умолчанию, мы установим его явно для демонстрации).

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

Затем начните транзакцию.

BEGIN;

В Терминале 1 прочитайте баланс Алисы.

SELECT balance FROM accounts WHERE name = 'Alice';

Запомните баланс. Теперь в Терминале 2 начните транзакцию и обновите баланс Алисы.

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
UPDATE accounts SET balance = 90.00 WHERE name = 'Alice';
COMMIT;

В Терминале 1 снова прочитайте баланс Алисы.

SELECT balance FROM accounts WHERE name = 'Alice';

Поскольку уровень изоляции равен READ COMMITTED, вы увидите обновленный баланс (90.00), зафиксированный транзакцией из Терминала 2.

Обновление баланса Алисы в READ COMMITTED

Теперь давайте проверим уровень изоляции REPEATABLE READ. В Терминале 1 откатите текущую транзакцию и установите уровень изоляции на REPEATABLE READ.

ROLLBACK;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;

В Терминале 1 снова прочитайте баланс Алисы.

SELECT balance FROM accounts WHERE name = 'Alice';

Запомните баланс. Теперь в Терминале 2 начните транзакцию и снова обновите баланс Алисы.

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

В Терминале 1 снова прочитайте баланс Алисы.

SELECT balance FROM accounts WHERE name = 'Alice';

Поскольку уровень изоляции равен REPEATABLE READ, вы по-прежнему увидите исходный баланс, существовавший на момент начала транзакции в Терминале 1, даже если в Терминале 2 было зафиксировано новое значение.

Наконец, в Терминале 1 зафиксируйте транзакцию.

COMMIT;

Теперь, если вы снова прочитаете баланс Алисы в Терминале 1, вы увидите последнее зафиксированное значение (100.00).

Изображение уровня изоляции repeatable read

Выйдите из обоих psql терминалов.

\q

Моделирование блокировок при одновременных обновлениях

В этом шаге вы смоделируете блокировки при одновременных обновлениях в PostgreSQL. Блокировки — это механизмы, используемые для предотвращения конфликтов между одновременными транзакциями, обеспечивая целостность данных.

Вы должны быть подключены к базе данных PostgreSQL из предыдущего шага. Если нет, подключитесь снова, используя:

sudo -u postgres psql

Откройте два отдельных терминала. В каждом терминале подключитесь к базе данных PostgreSQL как пользователь postgres. У вас должно быть два приглашения postgres=#.

Терминал 1:

sudo -u postgres psql

Терминал 2:

sudo -u postgres psql

В Терминале 1 начните транзакцию и обновите баланс Алисы. Важно использовать SELECT ... FOR UPDATE, чтобы заблокировать строку.

BEGIN;
SELECT balance FROM accounts WHERE name = 'Alice' FOR UPDATE;

Эта команда извлекает баланс Алисы и накладывает блокировку на строку, предотвращая другие транзакции от изменения её, пока эта транзакция не будет подтверждена или отменена.

В Терминале 2 начните транзакцию и попытайтесь обновить баланс Алисы.

BEGIN;
UPDATE accounts SET balance = balance + 10 WHERE name = 'Alice';

Обратите внимание, что эта команда в Терминале 2 может зависнуть. Это происходит потому, что она ожидает освобождения блокировки, удерживаемой Терминалом 1.

Теперь в Терминале 1 подтвердите транзакцию.

COMMIT;

После подтверждения транзакции в Терминале 1 команда UPDATE в Терминале 2 продолжится.

В Терминале 2 подтвердите транзакцию.

COMMIT;

Теперь в любом терминале запросите таблицу accounts, чтобы проверить изменения.

SELECT * FROM accounts;

Вы должны увидеть, что баланс Алисы был обновлён транзакцией в Терминале 2 после того, как Терминал 1 освободил блокировку.

Моделирование одновременного обновления в PostgreSQL

Наконец, выйдите из обоих psql терминалов.

\q

Этот пример демонстрирует, как SELECT ... FOR UPDATE можно использовать для моделирования блокировок и предотвращения конфликтов при одновременных обновлениях. Без блокировки обе транзакции могли бы прочитать один и тот же начальный баланс и применить свои обновления на основе этого значения, что приведёт к потере обновления.

Резюме

В этом лабораторном практикуме вы изучили управление транзакциями в PostgreSQL. Вы начали с подключения к базе данных PostgreSQL с помощью psql и создания образцовой таблицы accounts с начальными данными.

Затем вы сосредоточились на демонстрации использования транзакций. Вы узнали, как начать транзакцию с помощью команды BEGIN, выполнить несколько операций с базой данных (обновить балансы Алисы и Боба), а затем зафиксировать транзакцию с помощью команды COMMIT, чтобы сделать изменения постоянными. Это продемонстрировало фундаментальный принцип транзакций: рассматривать серию операций как единое атомарное целое. Вы также изучили, как откатывать транзакции и устанавливать уровни изоляции. Наконец, вы смоделировали блокировки при одновременных обновлениях, чтобы понять, как предотвратить повреждение данных.