Ответ
Уровни изоляции транзакций — это настройка в СУБД, которая определяет, насколько одна транзакция "видит" изменения, сделанные в других параллельных транзакциях. Это компромисс между согласованностью данных и производительностью.
Стандарт SQL определяет 4 основных уровня, каждый из которых решает определенные проблемы конкурентного доступа:
- Read Uncommitted: Самый слабый уровень. Позволяет читать незафиксированные ("грязные") данные. Используется редко из-за непредсказуемых результатов.
- Read Committed: Предотвращает "грязное чтение". Транзакция видит только те данные, которые были зафиксированы (committed) до её начала. Это уровень по умолчанию для многих СУБД (например, PostgreSQL, Oracle).
- Repeatable Read: Предотвращает "грязное" и "неповторяемое чтение" (non-repeatable read). Если транзакция читает одну и ту же строку несколько раз, она получит один и тот же результат. Уровень по умолчанию в MySQL.
- Serializable: Самый строгий уровень. Гарантирует полную изоляцию транзакций, как если бы они выполнялись последовательно. Предотвращает все аномалии, включая "фантомное чтение" (phantom reads), но может снижать производительность и приводить к ошибкам сериализации.
Как работать в Go:
В Go уровень изоляции можно указать при создании транзакции с помощью sql.TxOptions
. Пакет database/sql
предоставляет константы для стандартных уровней.
import (
"context"
"database/sql"
"log"
)
func performTransaction(db *sql.DB) {
ctx := context.Background()
// Начинаем транзакцию с уровнем изоляции Repeatable Read
tx, err := db.BeginTx(ctx, &sql.TxOptions{
Isolation: sql.LevelRepeatableRead,
})
if err != nil {
log.Fatal(err)
}
// Выполняем операции внутри транзакции
_, err = tx.ExecContext(ctx, "UPDATE accounts SET balance = balance - 100 WHERE id = 1")
if err != nil {
// Если произошла ошибка, откатываем транзакцию
tx.Rollback()
log.Fatal(err)
}
// Если все успешно, фиксируем транзакцию
if err := tx.Commit(); err != nil {
log.Fatal(err)
}
}