Ответ
Уровни изоляции транзакций — это настройка в СУБД, которая определяет, насколько одна транзакция "видит" изменения, сделанные в параллельных транзакциях. Стандарт SQL определяет четыре основных уровня:
Read Uncommitted (Чтение незафиксированных данных)
- Описание: Самый низкий уровень. Транзакция может читать данные, которые были изменены другой транзакцией, но еще не зафиксированы (не применены через
COMMIT
). - Проблема: Позволяет "грязное чтение" (dirty read).
- Описание: Самый низкий уровень. Транзакция может читать данные, которые были изменены другой транзакцией, но еще не зафиксированы (не применены через
Read Committed (Чтение зафиксированных данных)
- Описание: Транзакция видит только те изменения, которые были зафиксированы другими транзакциями. Это уровень по умолчанию для многих СУБД (например, PostgreSQL, Oracle).
- Проблема: Защищает от "грязного чтения", но возможна проблема "неповторяемого чтения" (non-repeatable read) — повторное чтение одной и той же строки в рамках одной транзакции может вернуть разные данные.
Repeatable Read (Повторяемое чтение)
- Описание: Гарантирует, что если транзакция повторно читает одну и ту же строку, она получит те же самые данные. СУБД блокирует читаемые строки от изменений другими транзакциями.
- Проблема: Защищает от "неповторяемого чтения", но возможна проблема "фантомного чтения" (phantom read) — когда при повторном чтении диапазона строк появляются новые строки, добавленные другой транзакцией.
Serializable (Сериализуемый)
- Описание: Самый строгий уровень. Гарантирует, что результат параллельного выполнения транзакций будет таким же, как если бы они выполнялись последовательно. Защищает от всех вышеперечисленных проблем.
- Цена: Наибольшее влияние на производительность из-за использования блокировок.
Установка уровня изоляции в Go
В Go уровень изоляции указывается при старте транзакции с помощью объекта sql.TxOptions
. Неверно думать, что он устанавливается на всё соединение.
package main
import (
"context"
"database/sql"
"log"
)
func main() {
db, err := sql.Open("postgres", "your_connection_string")
if err != nil {
log.Fatal(err)
}
defer db.Close()
ctx := context.Background()
// Запускаем транзакцию с уровнем изоляции Serializable
txOpts := &sql.TxOptions{Isolation: sql.LevelSerializable}
tx, err := db.BeginTx(ctx, txOpts)
if err != nil {
log.Fatal(err)
}
// ... выполняем операции в транзакции ...
// Завершаем транзакцию
if err := tx.Commit(); err != nil {
log.Fatal(err) // или tx.Rollback()
}
}