added theory bd isolation level

This commit is contained in:
Dmitry Medvedev
2026-07-11 19:19:54 +05:00
parent e8cb1448dd
commit 40e5761f6e
@@ -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. А ТАКЖЕ УСЛОВИЕ, ЧТО БАЛАНС НЕ МОЖЕТ ОПУСКАТЬСЯ НИЖЕ НУЛЯ, ИНАЧЕ ОТКЛОНЕНИЕ ТРАНЗАКЦИИ.