Как SAVEPOINT меняет границу отката внутри одной SQL-транзакции?
SAVEPOINT создаёт промежуточную точку, до которой можно откатить часть изменений, не завершая всю транзакцию. Откат к такой точке отменяет операции после неё, но сохраняет изменения, выполненные до неё, а сама транзакция может продолжиться и затем быть зафиксирована.
Полный откат долго выполнявшейся транзакции из-за одной локальной ошибки не всегда практичен: он теряет и те изменения, которые были корректны. Механизм точек сохранения решает эту проблему, позволяя разделить одну транзакцию на логические участки с частичным восстановлением.
SAVEPOINT не превращает части транзакции в независимые транзакции. Все изменения по-прежнему подчиняются общей фиксации или окончательному откату.
Представим операцию импорта: сначала создаются корректные записи, затем обрабатывается необязательный блок данных, в котором возникает ошибка. Если выполнить полный откат, будут потеряны и корректные результаты первой части.
Если же просто игнорировать ошибку и продолжить работу без восстановления состояния, в транзакции могут остаться частичные изменения, нарушающие ожидаемую логику. Это создаёт риск зафиксировать неполный или противоречивый результат.
После создания SAVEPOINT СУБД запоминает логическую границу внутри текущей транзакции. Команда отката к этой точке отменяет изменения после неё и освобождает связанные с ними ресурсы в соответствии с правилами конкретной СУБД, но не откатывает более ранние изменения.
В примере заказ сохраняется, вставка необязательной позиции отменяется, затем транзакция фиксирует статус заказа как неполный. После отката к точке сохранения транзакция остаётся активной; при этом сама точка может сохраняться для повторного использования или быть освобождена явно — детали зависят от СУБД.
Важно отличать ROLLBACK TO SAVEPOINT от полного отката транзакции. Полный откат отменяет все изменения, а откат к точке отменяет только последующую часть. Если транзакция уже зафиксирована или полностью отменена, продолжить её нельзя.
Точки сохранения не устраняют блокировки и не создают изоляцию между участками одной транзакции. Также они не гарантируют одинаковое поведение для всех операторов: особенности отката DDL, автоматического отката при ошибках и состояния транзакции зависят от конкретной СУБД.
Частое ограничение — чрезмерное использование SAVEPOINT в больших циклах. Каждая точка добавляет служебные операции и усложняет анализ состояния, поэтому для независимых единиц работы обычно лучше использовать отдельные транзакции.
Сервис пакетно загружает счета. Основные данные счёта обязательны, а дополнительные метаданные могут быть отброшены, если они не проходят проверку.
Вариант с отдельной транзакцией для каждой части лучше изолирует ошибки и уменьшает продолжительность блокировок, но может привести к частично зафиксированному счёту: между транзакциями другая сессия уже увидит основной объект, а метаданные ещё не обработаны.
Вариант с одной транзакцией без SAVEPOINT сохраняет атомарность, но ошибка в необязательной части заставляет откатить весь пакет. Выбранное решение — одна транзакция с точкой сохранения перед необязательным блоком: обязательные данные фиксируются атомарно, проблемная часть откатывается локально, а счёт получает явно обозначенный неполный статус.
Результат — нет «висящих» промежуточных фиксаций, а бизнес-логика явно различает полный и частичный результат. При этом длительность транзакции и объём удерживаемых блокировок нужно контролировать отдельно.
1. Можно ли после ROLLBACK TO SAVEPOINT продолжить ту же транзакцию?
Да, обычно можно: откат к точке сохранения не завершает транзакцию. После него разрешены новые операции, а окончательный результат определяется последующим COMMIT или полным ROLLBACK. Однако конкретные правила обработки ошибок и некоторых операторов зависят от СУБД.
2. Делает ли SAVEPOINT изменения до него видимыми другим транзакциям?
Нет. Точка сохранения управляет только внутренним откатом, а не фиксацией данных. Изменения становятся доступными другим транзакциям по обычным правилам текущего уровня изоляции и, как правило, после общего COMMIT; SAVEPOINT не является промежуточной фиксацией.
3. Освобождает ли откат к SAVEPOINT все блокировки, полученные после этой точки?
Нельзя безоговорочно рассчитывать на полное освобождение всех таких блокировок. Поведение зависит от СУБД и типа блокировки: часть ресурсов может быть связана с изменениями, часть — с самой транзакцией или выполненным чтением. Поэтому SAVEPOINT не следует использовать как универсальный способ сократить блокировки; для этого нужно проверять документацию конкретной СУБД и проектировать более короткие транзакции.