Открыть сервис

Каскадный откат

Каскадный откат — это сбой в работе системы управления базами данных (СУБД), при котором транзакция, ранее прочитавшая или изменившая данные, отменяется (откатывается) из-за отката другой транзакции, от которой она зависела. Данное явление характерно для систем, использующих механизмы конкурентного доступа и блокировок, и представляет собой нежелательный эффект, снижающий производительность и предсказуемость работы базы данных.

Причины возникновения

Каскадный откат возникает в условиях, когда несколько транзакций одновременно работают с одними и теми же данными. Основной причиной является нарушение принципа изоляции транзакций (I — Isolation, одно из свойств ACID) на уровне, допускающем чтение незафиксированных данных (так называемое «грязное чтение», dirty read).

Механизм возникновения

  1. Транзакция A начинает выполнение и изменяет некоторую запись (например, изменяет баланс счета). Изменение ещё не зафиксировано (не выполнена команда COMMIT).
  2. Транзакция B читает или изменяет ту же самую запись, полагаясь на данные, предоставленные транзакцией A. Если уровень изоляции транзакции B допускает чтение незафиксированных данных (Read Uncommitted), она видит новое значение.
  3. Транзакция A по какой-либо причине откатывается (выполняется команда ROLLBACK). Все изменения, внесённые транзакцией A, отменяются.
  4. Транзакция B оказывается в состоянии, когда её данные (прочитанные или изменённые) основаны на отменённых данных. Для сохранения целостности базы данных транзакция B также должна быть откачена. Если транзакция B, в свою очередь, повлияла на другие транзакции (C, D и т.д.), откат распространяется дальше, образуя «каскад».

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

Влияние на производительность и целостность

Каскадный откат является крайне нежелательным явлением по нескольким причинам:

  • Потеря работы: Все транзакции, попавшие в каскад, отменяются, даже если они были логически корректны и не содержали ошибок. Пользователь или приложение теряют все изменения, внесённые в рамках этих транзакций.
  • Снижение пропускной способности: Система тратит ресурсы на выполнение работы, которая впоследствии будет отменена. Это снижает общую производительность базы данных, особенно при высокой конкурентной нагрузке.
  • Усложнение восстановления: Механизмы отката становятся более сложными, так как системе необходимо отследить все зависимости между транзакциями и корректно отменить их в правильном порядке.
  • Непредсказуемость: Разработчики приложений не могут гарантировать, что транзакция, выполнившая чтение данных, будет успешно завершена, если она зависит от другой, ещё не завершённой транзакции.

Способы предотвращения

Основным способом предотвращения каскадных откатов является использование механизмов управления конкурентным доступом, которые не допускают чтения незафиксированных данных. Наиболее распространённые подходы:

1. Блокировки (Pessimistic Locking)

При использовании строгих двухфазных блокировок (Strict Two-Phase Locking, S2PL) транзакция, изменяющая данные, удерживает исключительную блокировку на эти данные до момента своего завершения (COMMIT или ROLLBACK). Другие транзакции не могут прочитать или изменить эти данные, пока блокировка не будет снята. Это полностью исключает каскадные откаты, так как никакая другая транзакция не может увидеть незафиксированные изменения. Однако этот подход снижает уровень конкурентности, так как другие транзакции вынуждены ждать.

2. Уровни изоляции транзакций

Стандарт SQL определяет несколько уровней изоляции. Для предотвращения каскадных откатов необходимо использовать уровень, запрещающий «грязное чтение»:

  • Read Committed (чтение зафиксированных данных): Транзакция видит только те данные, которые были зафиксированы другими транзакциями. Это стандартный уровень изоляции в большинстве современных СУБД (например, PostgreSQL, Oracle, Microsoft SQL Server). Он гарантирует, что транзакция никогда не прочитает незафиксированные данные, что исключает каскадные откаты, вызванные чтением.
  • Repeatable Read (повторяемое чтение): Гарантирует, что если транзакция прочитала данные, то при повторном чтении в рамках той же транзакции она увидит те же самые значения, даже если другая транзакция их изменила и зафиксировала. Этот уровень также предотвращает каскадные откаты.
  • Serializable (сериализуемый): Самый строгий уровень, при котором транзакции выполняются так, как если бы они были последовательными. Полностью исключает любые аномалии конкурентного доступа, включая каскадные откаты.

3. Версионность (Multiversion Concurrency Control, MVCC)

Многие современные СУБД (PostgreSQL, MySQL с InnoDB, Oracle) используют механизм многоверсионного управления конкурентным доступом (MVCC). Вместо блокировки данных при изменении создаётся новая версия записи. Транзакции работают со снимком данных (snapshot), который был актуален на момент начала транзакции. Это позволяет читающим транзакциям не блокироваться пишущими и наоборот. Поскольку читающая транзакция всегда видит только зафиксированную версию данных (или версию на момент своего начала), она никогда не зависит от незафиксированных изменений, что полностью исключает каскадные откаты, вызванные чтением. Однако каскадные откаты всё ещё возможны, если одна транзакция изменяет данные, а затем другая транзакция изменяет те же данные, полагаясь на первое изменение, и первая транзакция откатывается. В таких случаях СУБД может выявить конфликт и принудительно откатить одну из транзакций (часто ту, которая завершилась позже), что также является формой каскадного отката, но управляемой.

Пример на практике

Рассмотрим простой пример с банковским счётом.

  1. Транзакция A: UPDATE accounts SET balance = balance - 100 WHERE id = 1; (Снимает 100 рублей со счёта 1). Изменение не зафиксировано.
  2. Транзакция B (уровень Read Uncommitted): SELECT balance FROM accounts WHERE id = 1; (Читает баланс счёта 1). Видит значение, уменьшенное на 100 рублей.
  3. Транзакция A: ROLLBACK; (Откат). Баланс счёта 1 возвращается к исходному значению.
  4. Результат: Транзакция B работала с неверными данными. Если бы она на основе прочитанного значения приняла решение (например, перевела деньги на другой счёт), то её также пришлось бы откатить, чтобы не допустить расхождения баланса. Это и есть каскадный откат.

Если бы транзакция B использовала уровень изоляции Read Committed, она бы не увидела незафиксированное изменение транзакции A и, следовательно, не зависела бы от неё. После отката A транзакция B продолжила бы работу с корректными данными.

Заключение

Каскадный откат является артефактом конкурентного доступа к данным, который может привести к значительным потерям производительности и сложностям в управлении транзакциями. Современные СУБД предоставляют эффективные механизмы (уровни изоляции, MVCC, блокировки) для его предотвращения, что позволяет разработчикам строить надёжные и масштабируемые приложения, работающие с базами данных. Выбор конкретного метода зависит от требований к производительности и целостности данных конкретного приложения.

Источники:

  • К. Дж. Дейт. «Введение в системы баз данных» (8-е издание).
  • Гектор Гарсия-Молина, Джеффри Д. Ульман, Дженнифер Уидом. «Системы баз данных. Полный курс».
  • Стандарт SQL:2016 (ISO/IEC 9075).
  • Документация PostgreSQL, MySQL, Microsoft SQL Server и Oracle по управлению конкурентным доступом и уровням изоляции транзакций.

BFOmetr — база данных и аналитика по компаниям России.

На главную BFOmetr →