Isolation levels
SQL стандарт (ANSI SQL-92) описывает 4 уровня изоляции транзакций. Определяют какие аномалии возможны при параллельном выполнении.
Аномалии
| Аномалия | Что |
|---|---|
| Dirty read | чтение незакоммиченных данных |
| Non-repeatable read | две одинаковые SELECT в одной tx дают разные значения (кто-то UPDATE закоммитил) |
| Phantom read | две одинаковые SELECT дают разное число строк (кто-то INSERT/DELETE закоммитил) |
| Write skew | две tx читают одни данные, пишут не пересекающиеся, нарушают инвариант |
| Lost update | две tx читают, обе изменяют, второй перезаписывает первый |
Уровни по стандарту
| Уровень | Dirty | Non-repeat | Phantom |
|---|---|---|---|
| READ UNCOMMITTED | возможен | возможен | возможен |
| READ COMMITTED | - | возможен | возможен |
| REPEATABLE READ | - | - | возможен* |
| SERIALIZABLE | - | - | - |
*В PostgreSQL и InnoDB REPEATABLE READ фактически не имеет phantom.
Реальные дефолты
| БД | Default |
|---|---|
| PostgreSQL | READ COMMITTED |
| MySQL InnoDB | REPEATABLE READ |
| Oracle | READ COMMITTED |
| SQL Server | READ COMMITTED |
| CockroachDB | SERIALIZABLE (единственный) |
READ COMMITTED (PG default)
Каждый оператор видит свежий snapshot. Две SELECT подряд могут вернуть разное.
T1: BEGIN
T1: SELECT balance FROM acc WHERE id=1 → 100
T2: UPDATE acc SET balance=200 WHERE id=1
T2: COMMIT
T1: SELECT balance FROM acc WHERE id=1 → 200 ← non-repeatable read
T1: COMMIT
REPEATABLE READ
Одна snapshot на всю транзакцию. Две SELECT дадут одно значение.
T1: BEGIN ISOLATION LEVEL REPEATABLE READ
T1: SELECT balance → 100
T2: UPDATE, COMMIT
T1: SELECT balance → 100 ← видим старое
T1: COMMIT
SERIALIZABLE
Транзакции выполняются как будто последовательно. Защита от всего, включая write skew.
PostgreSQL реализация — SSI (Serializable Snapshot Isolation, Michael Cahill 2008). Работает через отслеживание read/write dependencies. При коммите — если найден риск нарушения serializable — откат одной из tx.
T1 → ERROR: could not serialize access due to read/write dependencies among transactions
# приложение должно ретраить
SELECT ... FOR UPDATE
Явная блокировка row без повышения isolation. Часто проще чем SERIALIZABLE.
BEGIN;
SELECT balance FROM acc WHERE id=1 FOR UPDATE;
-- никто другой не UPDATE эту строку до COMMIT
UPDATE acc SET balance=balance-100 WHERE id=1;
COMMIT;
Snapshot vs Serializable
SI (snapshot isolation) не эквивалентен SERIALIZABLE. Уязвим к write skew. Oracle SERIALIZABLE на самом деле — SI, не настоящий SERIALIZABLE. PG — настоящий SERIALIZABLE (SSI).