Рассмотрите сценарий с транзакциями в БД: T1 читает, затем T2 обновляет ту же строку; опишите возможные аномалии при уровнях изоляции READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ и SERIALIZABLE и как они проявляются на примерах
Кратко по каждому уровню — какие аномалии возможны и короткие примеры (значение поля обозначено как vvv). READ UNCOMMITTED - Что допускает: грязные чтения, неповторяющиеся чтения, фантомы, потерянные обновления. - Пример грязного чтения: (1)(1)(1) Начальное: v=100v=100v=100. (2)(2)(2) T1: читает vvv → видит 100100100. (3)(3)(3) T2: обновляет vvv на 200200200 (еще не зафиксировал). (4)(4)(4) T1: читает снова → видит 200200200 (грязное чтение, т.к. T2 может откатить). (5)(5)(5) T2: откатывает → реальное vvv снова 100100100, но T1 уже работал с несуществующим состоянием. - Пример потерянного обновления: если T1 затем запишет v=150v=150v=150 на основе своего устаревшего чтения, обновление T2 может быть перезаписано без обнаружения. READ COMMITTED - Что запрещено/допускается: грязные чтения запрещены (чтение видит только зафиксированные значения); допускаются неповторяющиеся чтения и фантомы; возможны потерянные обновления без явной блокировки. - Пример неповторяющегося чтения: (1)(1)(1) Начальное: v=100v=100v=100. (2)(2)(2) T1: читает vvv → видит 100100100. (3)(3)(3) T2: обновляет vvv на 200200200 и коммитит. (4)(4)(4) T1: читает vvv снова → видит 200200200 (значение изменилось — неповторяющееся чтение). - Пример потерянного обновления: T1 и T2 читают vvv, оба вычисляют новое значение и оба пишут без синхронизации — одно обновление может перезаписать другое. REPEATABLE READ - Что запрещено/допускается: гарантирует, что повторные чтения одной и той же строки в рамках транзакции дают одно и то же значение (нет неповторяющихся чтений); допускаются фантомы по диапазонным запросам (в стандарте) — реализация зависит от СУБД (например, InnoDB часто предотвращает фантомы). Потерянные обновления обычно предотвращаются блокировками при записи, но зависит от поведения СУБД. - Пример гарантии повторяемости: (1)(1)(1) Начальное: v=100v=100v=100. (2)(2)(2) T1 (REPEATABLE READ): читает vvv → видит 100100100. (3)(3)(3) T2: обновляет vvv на 200200200 и коммитит. (4)(4)(4) T1: читает vvv снова → видит 100100100 (повторяемость чтения). T1 при попытке записать на основе старого значения либо столкнётся с конфликтом при коммите, либо перезапишет в зависимости от СУБД; в стандарте ожидается предотвращение некорректных эффектов. - Фантом: если T1 делает диапазонный SELECT, T2 может вставить новую строку, и повторный диапазонный SELECT в T1 может вернуть дополнительные строки (в стандарте — фантомы возможны). SERIALIZABLE - Что запрещено: все вышеописанные аномалии (грязные, неповторяющиеся, фантомы, потерянные обновления) устранены — поведение эквивалентно некоторой сериализации транзакций. - Пример: (1)(1)(1) Начальное: v=100v=100v=100. (2)(2)(2) T1: читает vvv → видит 100100100. (3)(3)(3) Если T2 пытается изменить vvv одновременно, СУБД либо заставит T2 ждать, либо откатит одну из транзакций, чтобы результат был как при последовательном выполнении (либо T1 затем T2, либо T2 затем T1). Никаких грязных или непредвиденных смен значений не будет — окончательное состояние равно одному из последовательных порядков. Короткие итоги: - READ UNCOMMITTED — всё возможно (включая грязные чтения). - READ COMMITTED — нет грязных чтений; допускаются неповторяющиеся чтения и фантомы. - REPEATABLE READ — строки читаются стабильно внутри транзакции (нет неповторяющихся чтений); фантомы зависят от реализации. - SERIALIZABLE — полный изолятор, все аномалии исключены.
READ UNCOMMITTED
- Что допускает: грязные чтения, неповторяющиеся чтения, фантомы, потерянные обновления.
- Пример грязного чтения:
(1)(1)(1) Начальное: v=100v=100v=100.
(2)(2)(2) T1: читает vvv → видит 100100100.
(3)(3)(3) T2: обновляет vvv на 200200200 (еще не зафиксировал).
(4)(4)(4) T1: читает снова → видит 200200200 (грязное чтение, т.к. T2 может откатить).
(5)(5)(5) T2: откатывает → реальное vvv снова 100100100, но T1 уже работал с несуществующим состоянием.
- Пример потерянного обновления: если T1 затем запишет v=150v=150v=150 на основе своего устаревшего чтения, обновление T2 может быть перезаписано без обнаружения.
READ COMMITTED
- Что запрещено/допускается: грязные чтения запрещены (чтение видит только зафиксированные значения); допускаются неповторяющиеся чтения и фантомы; возможны потерянные обновления без явной блокировки.
- Пример неповторяющегося чтения:
(1)(1)(1) Начальное: v=100v=100v=100.
(2)(2)(2) T1: читает vvv → видит 100100100.
(3)(3)(3) T2: обновляет vvv на 200200200 и коммитит.
(4)(4)(4) T1: читает vvv снова → видит 200200200 (значение изменилось — неповторяющееся чтение).
- Пример потерянного обновления: T1 и T2 читают vvv, оба вычисляют новое значение и оба пишут без синхронизации — одно обновление может перезаписать другое.
REPEATABLE READ
- Что запрещено/допускается: гарантирует, что повторные чтения одной и той же строки в рамках транзакции дают одно и то же значение (нет неповторяющихся чтений); допускаются фантомы по диапазонным запросам (в стандарте) — реализация зависит от СУБД (например, InnoDB часто предотвращает фантомы). Потерянные обновления обычно предотвращаются блокировками при записи, но зависит от поведения СУБД.
- Пример гарантии повторяемости:
(1)(1)(1) Начальное: v=100v=100v=100.
(2)(2)(2) T1 (REPEATABLE READ): читает vvv → видит 100100100.
(3)(3)(3) T2: обновляет vvv на 200200200 и коммитит.
(4)(4)(4) T1: читает vvv снова → видит 100100100 (повторяемость чтения). T1 при попытке записать на основе старого значения либо столкнётся с конфликтом при коммите, либо перезапишет в зависимости от СУБД; в стандарте ожидается предотвращение некорректных эффектов.
- Фантом: если T1 делает диапазонный SELECT, T2 может вставить новую строку, и повторный диапазонный SELECT в T1 может вернуть дополнительные строки (в стандарте — фантомы возможны).
SERIALIZABLE
- Что запрещено: все вышеописанные аномалии (грязные, неповторяющиеся, фантомы, потерянные обновления) устранены — поведение эквивалентно некоторой сериализации транзакций.
- Пример:
(1)(1)(1) Начальное: v=100v=100v=100.
(2)(2)(2) T1: читает vvv → видит 100100100.
(3)(3)(3) Если T2 пытается изменить vvv одновременно, СУБД либо заставит T2 ждать, либо откатит одну из транзакций, чтобы результат был как при последовательном выполнении (либо T1 затем T2, либо T2 затем T1). Никаких грязных или непредвиденных смен значений не будет — окончательное состояние равно одному из последовательных порядков.
Короткие итоги:
- READ UNCOMMITTED — всё возможно (включая грязные чтения).
- READ COMMITTED — нет грязных чтений; допускаются неповторяющиеся чтения и фантомы.
- REPEATABLE READ — строки читаются стабильно внутри транзакции (нет неповторяющихся чтений); фантомы зависят от реализации.
- SERIALIZABLE — полный изолятор, все аномалии исключены.