Какую проблему решают транзакции в базах данных?

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

Ответ

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

Свойства ACID:

  • A (Atomicity) — Атомарность: Транзакция выполняется полностью или не выполняется вовсе. Нет промежуточных состояний.
  • C (Consistency) — Согласованность: Транзакция переводит базу данных из одного валидного состояния в другое валидное состояние, соблюдая все бизнес-правила и ограничения (constraints).
  • I (Isolation) — Изолированность: Параллельно выполняющиеся транзакции не должны влиять друг на друга. Уровни изоляции (Read Uncommitted, Read Committed, Repeatable Read, Serializable) регулируют степень этой изоляции.
  • D (Durability) — Долговечность: После успешного завершения (commit) результаты транзакции сохраняются в базе данных навсегда, даже в случае сбоя системы.

Практический пример (перевод денег):

// Без транзакции: риск несогласованного состояния при ошибке
update accounts set balance = balance - 100 where id = 1;
// Если здесь произойдет сбой, деньги спишутся, но не зачислятся
update accounts set balance = balance + 100 where id = 2;

// С транзакцией в JDBC:
Connection conn = dataSource.getConnection();
try {
    conn.setAutoCommit(false); // Начало транзакции

    PreparedStatement stmt1 = conn.prepareStatement(
        "UPDATE accounts SET balance = balance - ? WHERE id = ?");
    stmt1.setInt(1, 100); stmt1.setInt(2, 1);
    stmt1.executeUpdate();

    PreparedStatement stmt2 = conn.prepareStatement(
        "UPDATE accounts SET balance = balance + ? WHERE id = ?");
    stmt2.setInt(1, 100); stmt2.setInt(2, 2);
    stmt2.executeUpdate();

    conn.commit(); // Фиксация изменений. Только здесь данные сохраняются.
} catch (SQLException e) {
    conn.rollback(); // Откат всех изменений в блоке try при любой ошибке
    throw e;
} finally {
    conn.setAutoCommit(true);
    conn.close();
}