Files
homework/dmedvedev/2.7.theory_bd_isolation_levels.sql
2026-07-11 19:19:54 +05:00

87 lines
4.3 KiB
PL/PgSQL
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
- Задание 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. А ТАКЖЕ УСЛОВИЕ, ЧТО БАЛАНС НЕ МОЖЕТ ОПУСКАТЬСЯ НИЖЕ НУЛЯ, ИНАЧЕ ОТКЛОНЕНИЕ ТРАНЗАКЦИИ.