Что такое уровни изоляции транзакций в базах данных?

«Что такое уровни изоляции транзакций в базах данных?» — вопрос из категории Базы данных, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Уровни изоляции транзакций определяют степень видимости изменений, выполняемых параллельными транзакциями, и защищают от стандартных проблем конкурентного доступа. Выбор уровня — это компромисс между согласованностью данных и производительностью (параллелизмом).

Основные проблемы, от которых защищают уровни изоляции:

  1. Грязное чтение (Dirty Read): Чтение незафиксированных данных другой транзакции, которые могут быть откатаны.
  2. Неповторяющееся чтение (Non-repeatable Read): Получение разных значений одного и того же поля при повторном чтении в рамках одной транзакции из-за коммита другой транзакции.
  3. Фантомное чтение (Phantom Read): Появление новых или исчезновение существующих строк при повторном выполнении одного и того же запроса из-за коммита другой транзакции.
Уровни изоляции (от меньшей к большей): Уровень Грязное чтение Неповторяющееся чтение Фантомное чтение Механизм
Read Uncommitted ❌ Возможно Возможно Возможно Блокировок на чтение нет.
Read Committed (по умолчанию в SQL Server, PostgreSQL) ✅ Защищено ❌ Возможно Возможно Блокировка изменяемой строки до коммита.
Repeatable Read ✅ Защищено ✅ Защищено ❌ Возможно Блокировка читаемых и изменяемых строк.
Serializable ✅ Защищено ✅ Защищено ✅ Защищено Блокировка диапазонов ключей или таблиц.

Пример использования в C# (Entity Framework Core):

using var context = new AppDbContext();
using var transaction = await context.Database.BeginTransactionAsync(
    System.Data.IsolationLevel.Serializable // Самый строгий уровень
);

try
{
    // Критическая операция, требующая полной изоляции
    var balance = await context.Accounts
        .Where(a => a.Id == accountId)
        .Select(a => a.Balance)
        .FirstAsync();

    if (balance >= amount)
    {
        // Списание средств...
        await context.SaveChangesAsync();
        await transaction.CommitAsync(); // Фиксация изменений
    }
}
catch (Exception)
{
    await transaction.RollbackAsync(); // Откат в случае ошибки
    throw;
}