Для чего нужны транзакции в базах данных?

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

Ответ

Транзакции — это механизм, гарантирующий корректное выполнение группы операций с данными, даже в условиях сбоев или параллельного доступа. Они обеспечивают соблюдение принципов ACID:

  • Атомарность (Atomicity): Все операции транзакции выполняются как единое целое. Либо выполняются все изменения (COMMIT), либо ни одного (ROLLBACK).
  • Согласованность (Consistency): Транзакция переводит базу данных из одного корректного состояния в другое, соблюдая все бизнес-правила и ограничения целостности (UNIQUE, FOREIGN KEY и т.д.).
  • Изолированность (Isolation): Параллельно выполняющиеся транзакции не должны мешать друг другу. Уровни изоляции (Read Committed, Serializable и др.) определяют степень этого разделения.
  • Долговечность (Durability): После подтверждения (COMMIT) результаты транзакции сохраняются на постоянное хранилище и не теряются даже при сбое системы.

Типичный сценарий — денежный перевод:

using (var connection = new SqlConnection(connectionString))
{
    await connection.OpenAsync();
    // Начало транзакции
    using (var transaction = await connection.BeginTransactionAsync())
    {
        try
        {
            // 1. Списать сумму со счета отправителя
            var cmd1 = new SqlCommand(
                "UPDATE Accounts SET Balance = Balance - @Amount WHERE Id = @FromId",
                connection, transaction);
            cmd1.Parameters.AddWithValue("@Amount", 100.00m);
            cmd1.Parameters.AddWithValue("@FromId", 1);
            await cmd1.ExecuteNonQueryAsync();

            // 2. Зачислить сумму на счет получателя
            var cmd2 = new SqlCommand(
                "UPDATE Accounts SET Balance = Balance + @Amount WHERE Id = @ToId",
                connection, transaction);
            cmd2.Parameters.AddWithValue("@Amount", 100.00m);
            cmd2.Parameters.AddWithValue("@ToId", 2);
            await cmd2.ExecuteNonQueryAsync();

            // Если оба UPDATE прошли успешно — подтверждаем
            await transaction.CommitAsync();
            Console.WriteLine("Перевод выполнен.");
        }
        catch (Exception ex)
        {
            // При любой ошибке откатываем оба изменения
            await transaction.RollbackAsync();
            Console.WriteLine($"Ошибка перевода: {ex.Message}. Транзакция отменена.");
            throw;
        }
    }
}

Без транзакции сбой после первого UPDATE привел бы к потере денег. Транзакция гарантирует, что либо оба обновления пройдут, либо балансы останутся неизменными.