Транзакции и конкурентный доступ
Транзакция объединяет операторы в одно атомарное изменение. RadixDB использует
многоверсионное управление конкурентным доступом (MVCC): читатели выбирают
видимые зафиксированные версии строк, а незавершённые версии записи остаются
приватными. Эта глава описывает фактический контракт 1.2, включая различия
между READ COMMITTED и SNAPSHOT.
Автоматические и явные транзакции
Заголовок раздела «Автоматические и явные транзакции»Без BEGIN каждый оператор выполняется в собственной транзакции. Явная
транзакция нужна, когда несколько операторов должны либо стать видимыми вместе,
либо целиком откатиться. Следующий перевод сохраняет общий баланс 160.
CREATE TABLE accounts ( id INTEGER PRIMARY KEY, balance INTEGER NOT NULL);INSERT INTO accounts VALUES (1, 100), (2, 60);
BEGIN;UPDATE accounts SET balance = balance - 25 WHERE id = 1;UPDATE accounts SET balance = balance + 25 WHERE id = 2;SELECT id, balance FROM accounts ORDER BY id;COMMIT;
SELECT SUM(balance) AS total_balance FROM accounts;Транзакция видит собственные изменения: SELECT перед COMMIT возвращает
балансы 75 и 85. Другие подключения не видят ни одного обновления до успешной
фиксации. После COMMIT оба обновления становятся видимыми вместе.
ROLLBACK отменяет все изменения после BEGIN и завершает транзакцию.
BEGIN;UPDATE accounts SET balance = 0 WHERE id = 1;ROLLBACK;
SELECT id, balance FROM accounts ORDER BY id;Балансы остаются равны 75 и 85. Уничтожение embedded-дескриптора Transaction
без фиксации также вызывает откат, но приложение всё равно должно явно завершать
каждую транзакцию.
Точки сохранения
Заголовок раздела «Точки сохранения»Точка сохранения отмечает позицию внутри текущей транзакции. Откат к ней отменяет
более позднюю работу, но сохраняет сделанное ранее. Целевая точка остаётся
доступной до RELEASE или завершения транзакции.
BEGIN;UPDATE accounts SET balance = 90 WHERE id = 1;SAVEPOINT before_fee;UPDATE accounts SET balance = balance - 10 WHERE id = 1;ROLLBACK TO SAVEPOINT before_fee;RELEASE SAVEPOINT before_fee;COMMIT;
SELECT balance FROM accounts WHERE id = 1;Итоговый баланс равен 90. Имена точек без кавычек нечувствительны к регистру.
Сетевой клиент использует отдельные методы savepoint,
rollback_to_savepoint и release_savepoint; см. раздел
«Клиентские интерфейсы».
Видимость MVCC
Заголовок раздела «Видимость MVCC»Незафиксированные INSERT, UPDATE и DELETE видны сделавшей их транзакции и невидимы остальным. Откат удаляет приватную версию, а фиксация публикует транзакцию одной границей видимости. Читатели не получают грязные данные.
Для каждого примера изоляции ниже создайте новую базу и подготовьте таблицу до открытия двух подключений:
CREATE TABLE isolation_demo ( id INTEGER PRIMARY KEY, value INTEGER NOT NULL);INSERT INTO isolation_demo VALUES (1, 10), (2, 20);Видимость MVCC не устраняет конфликты записи. Она позволяет читателям продолжать работу с подходящей зафиксированной версией, пока движок отдельно координирует конкурирующих писателей.
Read committed
Заголовок раздела «Read committed»READ COMMITTED применяется по умолчанию. Каждый оператор видит данные,
зафиксированные до начала этого оператора, и собственные изменения транзакции.
Поэтому два чтения в одной транзакции могут увидеть разные зафиксированные
состояния.
-- Connection ABEGIN ISOLATION LEVEL READ COMMITTED;SELECT SUM(value) FROM isolation_demo; -- 30
-- Connection BBEGIN;UPDATE isolation_demo SET value = value + 100;COMMIT;
-- Connection ASELECT SUM(value) FROM isolation_demo; -- 230ROLLBACK;Подключение A не видит незавершённые изменения B. Его второй SELECT начинается после фиксации B и переходит к новому зафиксированному состоянию.
Snapshot isolation
Заголовок раздела «Snapshot isolation»SNAPSHOT закрепляет зафиксированное представление на момент начала транзакции.
Более поздние фиксации других подключений остаются за его пределами, но
транзакция по-прежнему видит собственные изменения.
-- Connection ABEGIN ISOLATION LEVEL SNAPSHOT;SELECT SUM(value) FROM isolation_demo; -- 30
-- Connection BBEGIN;UPDATE isolation_demo SET value = value + 100;COMMIT;
-- Connection ASELECT SUM(value) FROM isolation_demo; -- still 30ROLLBACK;
-- Connection A, after ending the snapshotSELECT SUM(value) FROM isolation_demo; -- 230Snapshot isolation не является сериализуемой изоляцией. Этот режим даёт стабильное представление для чтения и обнаруживает применимые конфликты записи, но приложение не должно считать, что он предотвращает все аномалии с записью в разные строки.
Конкурирующие писатели и повторы
Заголовок раздела «Конкурирующие писатели и повторы»Писатели, претендующие на одну строку или уникальный ключ, ожидают текущего
владельца. Рабочий бюджет ожидания одного неизменного владельца равен 10 секундам.
После фиксации или отката владельца ожидающий UPDATE повторно проверяет актуальную
зафиксированную строку и условие WHERE, а затем применяет изменение.
Цикл в общей для движка диаграмме ожиданий завершает одного участника ошибкой
serialization conflict. Неизменный блокировщик, превысивший бюджет, приводит
к row-lock timeout. Обе ошибки допускают повтор всей транзакции: выполните
ROLLBACK, примените задержку и начните заново с BEGIN. Нельзя повторять
только отклонённый оператор.
Нарушение ограничения также возвращает явную ошибку. Сетевая транзакция после
отклонённого оператора сохраняет возможность отката. Проверьте её состояние и
выполните ROLLBACK перед повторным использованием подключения, если API явно
не сообщил, что транзакция уже завершена.
BEGIN;INSERT INTO accounts VALUES (3, 10);INSERT INTO accounts VALUES (3, 20); -- primary-key errorROLLBACK;Первая вставка не публикуется. DDL и DML могут входить в одну явную транзакцию; изменения каталога остаются приватными для владельца до фиксации.
Поддерживаемые границы изоляции
Заголовок раздела «Поддерживаемые границы изоляции»SQL executor версии 1.2 принимает только READ COMMITTED и SNAPSHOT.
Следующие уровни отклоняются, а не подменяются более слабой изоляцией:
BEGIN ISOLATION LEVEL SERIALIZABLE;BEGIN ISOLATION LEVEL REPEATABLE READ;BEGIN ISOLATION LEVEL READ UNCOMMITTED;SET ISOLATIONLEVEL, SET ISOLATION_LEVEL и SET TRANSACTION_ISOLATION
меняют только default текущего подключения для будущих транзакций. Внутри
активной транзакции они отклоняются, поскольку её уровень уже зафиксирован
BEGIN. SHOW ISOLATION_LEVEL возвращает effective connection-local default;
другой handle того же движка не изменяется.
Границы клиентов в 1.2
Заголовок раздела «Границы клиентов в 1.2»Embedded Rust API выбирает изоляцию методом begin_with_isolation. TCP-клиент
отправляет отдельное транзакционное сообщение через одноимённый метод; обычный
wire execute намеренно отклоняет SQL управления транзакциями. Оба интерфейса
также предоставляют отдельные методы фиксации, отката и точек сохранения.
CLI 1.2 сохраняет предложения READ COMMITTED и SNAPSHOT в BEGIN и
направляет SAVEPOINT, ROLLBACK TO и RELEASE в active transaction handle.
Неподдерживаемые уровни fail-closed отклоняются. При ошибке многооператорного
batch CLI выполняет rollback и не публикует успешный prefix.
Изоляция транзакции и долговечность являются разными контрактами. COMMIT
публикует одну атомарную транзакцию; настроенный режим синхронизации определяет,
когда записи хранилища принудительно попадают на долговечный носитель. Эти
параметры приведены в разделе администрирования.