From 40e5761f6ea8442409cce109ac0629e681b3cb3e Mon Sep 17 00:00:00 2001 From: Dmitry Medvedev Date: Sat, 11 Jul 2026 19:19:54 +0500 Subject: [PATCH] added theory bd isolation level --- dmedvedev/2.7.theory_bd_isolation_levels.sql | 87 ++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 dmedvedev/2.7.theory_bd_isolation_levels.sql diff --git a/dmedvedev/2.7.theory_bd_isolation_levels.sql b/dmedvedev/2.7.theory_bd_isolation_levels.sql new file mode 100644 index 0000000..f94e175 --- /dev/null +++ b/dmedvedev/2.7.theory_bd_isolation_levels.sql @@ -0,0 +1,87 @@ +- Задание 1: Разбери ситуацию + + У тебя две транзакции работают с таблицей `accounts`: + + **Транзакция A:** + + BEGIN; + SELECT balance FROM accounts WHERE id = 1; -- balance = 100 + -- ждёт 10 секунд + SELECT balance FROM accounts WHERE id = 1; -- balance = ??? + COMMIT; + + **Транзакция B (выполняется параллельно):** + + BEGIN; + UPDATE accounts SET balance = 200 WHERE id = 1; + COMMIT; + + **Вопросы:** + + 1. Какой баланс увидит транзакция A во втором SELECT на уровне **Read Committed**? + УВИДИТ 200 + 2. Какой баланс увидит транзакция A во втором SELECT на уровне **Repeatable Read**? + УВИДИТ 100 + 3. Объясни, почему результаты разные. + ПОТОМУ ЧТО УРОВНИ ИЗОЛЯЦИИ РАЗНЫЕ, В ПЕРВОМ СЛУЧАЕ ВИДИМ ТОЛЬКО ЗАКОММИЧЕННЫЕ ДАННЫЕ, А ВО ВТОРОМ ТЕ ДАННЫЕ, КОТОРЫЕ БЫЛИ В САМОМ НАЧАЛЕ ТРАЗАКЦИИ. + +- Задание 2: Практика + + Создай таблицу: + + CREATE TABLE test_isolation (id INT PRIMARY KEY, value INT); + INSERT INTO test_isolation VALUES (1, 100); + + Открой **две** вкладки в DBeaver. + + **В первой вкладке:** + + BEGIN; + SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; + SELECT value FROM test_isolation WHERE id = 1; + + **Во второй вкладке:** + + BEGIN; + UPDATE test_isolation SET value = 200 WHERE id = 1; + COMMIT; + + **Обратно в первой вкладке:** + + SELECT value FROM test_isolation WHERE id = 1; + COMMIT; + + **Вопрос:** Какое значение ты увидел во втором SELECT? Почему? + УВИДЕЛ 100, ПОТОМУ ЧТО dbeaver В ПЕРВОЙ ТРАНЗАКЦИИ ПОДДЕРЖИВАЕТ Repeatable Read И ТРАНЗАКЦИЯ НЕ БЫЛА ЗАКОММИЧЕНА. + + Повтори эксперимент с уровнем **Read Committed** вместо **Repeatable Read**. Что изменилось? + В ДАННОМ СЛУЧАЕ ИЗМЕНИЛСЯ ТИП ИЗОЛЯЦИИ И ТК ВТОРАЯ ТРАЗАКЦИЯ БЫЛА ЗАКОМИЧЕНА, ТО ВЫВОД ТРЕТЬЕГО ЗАПРОСА ПОКАЗАЛ ИМЕННО ПОСЛЕДНЕЕ ЗАКОМИЧЕННОЕ. + +- Задание 3: Найди ошибку + + Вася написал код для списания денег со счёта: + + BEGIN; + SET TRANSACTION ISOLATION LEVEL READ COMMITTED; + + -- Проверяем баланс + SELECT balance FROM accounts WHERE id = 1; -- balance = 100 + + -- Думаем 5 секунд (пользователь вводит сумму) + + -- Списываем деньги + UPDATE accounts SET balance = balance - 50 WHERE id = 1; + + COMMIT; + + **Проблема:** Пока Вася думал 5 секунд, другая транзакция списала 80 руб. Теперь баланс стал отрицательным + + **Вопросы:** + + 1. Почему так произошло? + ПОТОМУ ЧТО ВНУТРЬ ТРАНЗАКЦИИ ВОРВОЛАСЬ ДРУГАЯ, А UPDATE БЕРЕТ ЗНАЧЕНИЕ НА ДАННЫЙ МОМЕНТ. ЧТОБЫ ЭТОГО ИЗБЕЖАТЬ ВАСЕ НАДО БЫЛО В СВОЕЙ ТРАНЗАКЦИИ НАПИСТАЬЬ FOR UPDATE И ЕГО ЗНАЧЕНИЕ БЫ ЗАМОРОЗИЛОСЬ. + + 2. Какой уровень изоляции поможет избежать этой проблемы? + Serializable + 3. Как ещё можно решить эту проблему без смены уровня изоляции? (подсказка: проверяй баланс перед UPDATE) + КАК ВЫШЕ СКАЗАЛ МОЖНО ИСПОЛЬЗОВАТЬ FOR UPDATE. А ТАКЖЕ УСЛОВИЕ, ЧТО БАЛАНС НЕ МОЖЕТ ОПУСКАТЬСЯ НИЖЕ НУЛЯ, ИНАЧЕ ОТКЛОНЕНИЕ ТРАНЗАКЦИИ. \ No newline at end of file