Ответ
Изоляция — это одно из свойств ACID (Atomicity, Consistency, Isolation, Durability), которое определяет, как и в какой степени изменения, сделанные в одной транзакции, видны другим параллельным транзакциям. Основная цель — предотвратить аномалии конкурентного доступа.
Проблемы, которые решает изоляция:
- Грязное чтение (Dirty Read): Одна транзакция читает данные, которые были изменены другой транзакцией, но еще не зафиксированы (не
COMMIT
'нуты). Если вторая транзакция откатится (ROLLBACK
), первая будет работать с неверными, "грязными" данными. - Неповторяемое чтение (Non-repeatable Read): В ходе одной транзакции происходит повторное чтение тех же данных, но результат оказывается разным, так как другая транзакция успела изменить и зафиксировать эти данные.
- Фантомное чтение (Phantom Read): В ходе одной транзакции выполняется запрос с одним и тем же условием выборки, но результаты различаются, так как другая транзакция добавила или удалила строки, удовлетворяющие этому условию.
Стандартные уровни изоляции SQL:
- Read Uncommitted: Самый низкий уровень. Предотвращает только потерю обновлений, но допускает все три аномалии.
- Read Committed: Предотвращает грязное чтение. Транзакция видит только зафиксированные изменения. Это уровень по умолчанию для многих СУБД (например, PostgreSQL).
- Repeatable Read: Предотвращает грязное и неповторяемое чтение. Гарантирует, что если транзакция прочитала строку, эта строка не изменится до конца транзакции. Однако фантомы возможны.
- Serializable: Самый строгий уровень. Предотвращает все аномалии, включая фантомы. Гарантирует, что параллельные транзакции выполняются так, как если бы они выполнялись последовательно.
Пример в Go с database/sql
:
// Устанавливаем высокий уровень изоляции для важной операции
tx, err := db.BeginTx(ctx, &sql.TxOptions{
Isolation: sql.LevelSerializable,
})
if err != nil {
log.Fatal(err)
}
// defer Rollback откатит транзакцию, если Commit() не будет вызван
// (например, из-за паники или ошибки)
defer tx.Rollback()
// ... Операции внутри транзакции ...
_, err = tx.ExecContext(ctx, "UPDATE accounts SET balance = balance - 100 WHERE id = 1")
if err != nil {
// Rollback будет вызван через defer
log.Fatal(err)
}
// Фиксируем изменения
err = tx.Commit()
if err != nil {
log.Fatal(err)
}