Транзакции
Понятие транзакции и ее свойства
Транзакции - это фундаментальная концепция баз данных, обеспечивающая целостность данных.
Транзакция - это последовательность операций с базой данных, которая должна быть выполнена как единое целое по принципу “все или ничего”.
Стандартно принято выделять 4 базовых свойства транзакций, сокращенно ACID-свойства:
- Атомарность (Atomicity) - транзакция выполняется полностью или не выполняется вообще
- Согласованность (Consistency) - транзакция переводит БД из одного согласованного состояния в другое
- Изолированность (Isolation) - параллельные транзакции не мешают друг другу
- Долговечность (Durability) - результаты завершенной транзакции сохраняются даже после сбоев
Представьте, что вы разрабатываете информационную систему для банка. Основной сущностью является счет, а основной операцией – перевод денег между счетами.
Перевод заключается в том, чтобы уменьшить баланс одного счета и увеличить баланс другого счета на такую же сумму. В терминах SQL это означает, что нужно выполнить два запроса UPDATE. В простейшем случае, база данных и запросы к ней могли бы выглядеть так.
CREATE TABLE ACCOUNTS (
ACCOUNT_ID VARCHAR(20) PRIMARY KEY,
OWNER_NAME VARCHAR(100) NOT NULL,
BALANCE DECIMAL(15,2) NOT NULL CHECK (BALANCE >= 0)
);
-- Инициализируем тестовые данные
INSERT INTO ACCOUNTS VALUES ('10000001', 'Иванов И.И.', 5000.00);
INSERT INTO ACCOUNTS VALUES ('10000002', 'Петров П.П.', 3000.00);
CREATE PROCEDURE TRANSFER (
FROM_ACCOUNT VARCHAR(20),
TO_ACCOUNT VARCHAR(20),
AMOUNT DECIMAL(15,2))
RETURNS (RES BOOLEAN)
AS
BEGIN
RES = FALSE;
UPDATE ACCOUNTS
SET BALANCE = BALANCE - :AMOUNT
WHERE ACCOUNT_ID = :FROM_ACCOUNT AND BALANCE >= :AMOUNT;
IF (ROW_COUNT = 0) THEN
BEGIN
SUSPEND;
EXIT;
END
-- а что если здесь отключат свет?
UPDATE ACCOUNTS SET BALANCE = BALANCE + :AMOUNT WHERE ACCOUNT_ID = :TO_ACCOUNT;
RES = TRUE;
SUSPEND;
END;
SELECT * FROM TRANSFER('10000001', '10000002', 10000.00);
SELECT * FROM ACCOUNTS;
В примере мы видим комментарий. А что случится, если в этом месте действительно отключат электричество, произойдет какой-либо сбой, аппаратная ошибка? В промышленных транзакционных СУБД эти ситуации и обрабатывает сервер. Если в этом месте произойдет сбой, то в дальнейшем, когда БД снова станет доступна, она будет содержать данные, как если бы процедуру TRANSFER не вызывали.
Кроме этой проблемы, транзакции играют важную роль в изоляции работы множества пользователей в одной базе данных, с целью исключения аномалий параллельного доступа.
Аномалии параллельного доступа
- Потерянное обновление. Возникает при одновременном изменении одного блока данных разными транзакциями. При этом теряются все изменения, кроме последнего.
| Транзакция 1 | Транзакция 2 |
|---|---|
UPDATE T SET F=F+10 WHERE K=1 | UPDATE T SET F=F+15 WHERE K=1 |
При последовательном выполнении транзакций, значение поля F изменилось бы на 25. Но при параллельном доступе, обе транзакции могут прочитать старое значение F, добавить свое слагаемое и записать новый результат. В результате, новое значение F будет увеличено или на 10 или на 15, в зависимости от того, какая из транзакций будет подтверждена последней и затрет изменения параллельной транзакции.
- Грязное чтение (Dirty Read). Чтение незафиксированных изменений другой транзакции.
| Транзакция 1 | Транзакция 2 |
|---|---|
UPDATE T SET F=F+1 WHERE K=1 | |
SELECT F FROM T WHERE K=1 | |
ROLLBACK |
Транзакция 1 изменяет данные поля F, после транзакция 2 его читает, использует далее в своей работе, а после транзакция 1 не подтверждается и получится, что прочтенные транзакцией 1 незафиксированные данные никогда не существовали, но были использованы при работе.
- Неповторяющееся чтение (Non-repeatable Read). Разные значения при повторном чтении одной строки.
| Транзакция 1 | Транзакция 2 |
|---|---|
SELECT F FROM T WHERE K=1 | |
UPDATE T SET F=F+2 WHERE K=1 | |
COMMIT | |
SELECT F FROM T WHERE K=1 |
В транзакции 2 выбирается значение поля F, затем в транзакции 1 изменяется значение поля F. При повторной попытке выбора значения из поля F в транзакции 2 будет получен другой результат.
- Фантомное чтение (Phantom Read). Появление новых строк при повторном чтении диапазона.
| Транзакция 1 | Транзакция 2 |
|---|---|
SELECT SUM(F) FROM T | |
INSERT INTO T (K, F) VALUES (2, 30) | |
COMMIT | |
SELECT SUM(F) FROM T |
Транзакция 2 выполняет запрос, использующий все значения поля F. Затем транзакция 1 вставляет новую строку. Повторное выполнение запроса в транзакции 2 выдаст другой результат. От неповторяющегося чтения оно отличается тем, что данные, к которым было обращение, не изменились, но изменилось количество строк с данными.
Уровни изоляции
Уровень изоляции определяет, насколько транзакции изолированы друг от друга и какие аномалии возможны при параллельном выполнении.
Стандарт SQL определяет 4 основные уровни изоляции:
-
READ UNCOMMITTED (Чтение незафиксированных данных)
- Самый низкий уровень изоляции
- Возможны все типы аномалий:
-
READ COMMITTED (Чтение зафиксированных данных)
- По умолчанию в большинстве СУБД
- Запрещает:
- Потерянное обновление
- Грязное чтение
- Разрешает:
- Неповторяющееся чтение
- Фантомное чтение
-
REPEATABLE READ (Повторяемое чтение)
- Запрещает:
- Грязное чтение
- Неповторяющееся чтение
- Потерянное обновление
- Разрешает:
- Фантомное чтение
- Запрещает:
-
SERIALIZABLE (Упорядочиваемый)
- Самый строгий уровень
- Запрещает все аномалии
- Эквивалентен последовательному выполнению транзакций
Исторически Firebird и РЕД База Данных реализует следующие уровни изоляции:
-
READ COMMITTED. В полном объеме соответствует стандартному.
-
SNAPSHOT. Практически аналог REPEATABLE READ, но запрещает также и фантомное чтение. Все операции видят согласованный снимок данных на момент начала транзакции.
-
SNAPSHOT TABLE STABILITY. Практически аналог SERIALIZABLE. Блокирует читаемые таблицы от изменений другими транзакциями.
Старт транзакции
Для старта транзакции с необходимыми параметрами используется оператор SET TRANSACTION.
Запуск транзакций на выполнение осуществляется только клиентскими приложениями, но не сервером.
SET TRANSACTION
[READ WRITE | READ ONLY]
[ISOLATION LEVEL] [SNAPSHOT [TABLE STABILITY] | READ COMMITTED]
Все предложения в операторе SET TRANSACTION являются необязательными.
Если в операторе запуска транзакции на выполнение не задано никакого
предложения, то предполагается старт транзакции со значениями всех
характеристик по умолчанию.
SET TRANSACTION
READ WRITE
ISOLATION LEVEL SNAPSHOT
Транзакция с такими же характеристиками по умолчанию автоматически запускается системой, если пользователь, выполняя обращение к данным базы данных, не запустил никакой транзакции.
При старте транзакции сервер передает клиенту дескриптор транзакции (целое число).
Значение этого дескриптора средствами SQL можно получить, используя
контекстную переменную CURRENT_TRANSACTION.
Для транзакций существует два режима доступа к данным базы данных:
READ WRITE (по умолчанию) и READ ONLY.
В режиме READ ONLY в контексте данной транзакции могут выполняться
только операции выборки данных SELECT. Любая попытка изменения данных
в контексте такой транзакции приведет к исключениям базы данных.
Уровень изоляции запускаемой транзакции задается необязательным
предложением ISOLATION LEVEL. Это самая важная характеристика
транзакции, которая определяет ее основное поведение по отношению к
другим одновременно выполняющимся транзакциям.
Подтверждение транзакции
Чтобы подтвердить текущую транзакцию используется оператор COMMIT.
COMMIT;
При выполнении этого оператора подтверждаются все изменения в данных, выполненные в контексте данной транзакции (добавления, изменения, удаления). Новые версии записей становятся доступными для других процессов. Если в процессе подтверждения транзакции возникли ошибки в базе данных, то транзакция не подтверждается. Пользовательская программа должна обработать ошибочную ситуацию и заново подтвердить транзакцию или выполнить ее откат.
Отмена транзакции
Для отмены всех изменений, выполненных в контексте текущей транзакции, используется оператор ROLLBACK.
ROLLBACK;
При выполнении оператора отменяются все изменения данных базы данных
(добавление, изменение, удаление), выполненные под управлением этой
транзакции. Оператор ROLLBACK никогда не вызывает ошибок.
Контрольные вопросы
- Что такое транзакция?
- Назовите свойства транзакций?
- Назовите аномалии параллельного доступа к данным?
- Чем фантомное чтение отличается от неповторяющегося чтения?
- Назовите уровни изоляции транзакций?
- Какие уровни изоляции транзакций поддерживает РЕД База Данных?
- Какие аномалии параллельного доступа разрешены на каждом из них?
- Какие аномалии параллельного доступа запрещены на каждом из них?
- Какие параметры транзакций допустимы?
- Что произойдет, если при подтверждении транзакции возникнут ошибки?
- Что произойдет, если при отмене транзакции возникнут ошибки?