Что означает принцип ACID в контексте баз данных?

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

Ответ

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

Расшифровка принципа ACID:

Свойство Описание Пример нарушения
Atomicity
(Атомарность)
Транзакция выполняется как единое целое. Либо выполняются все ее операции, либо ни одна. Недопустимо частичное выполнение. При переводе денег списались средства с одного счета, но не зачислились на другой.
Consistency
(Согласованность)
Транзакция переводит базу данных из одного валидного состояния в другое, соблюдая все бизнес-правила, ограничения целостности (constraints), триггеры. После транзакции нарушилось ограничение уникальности или внешний ключ.
Isolation
(Изолированность)
Параллельно выполняемые транзакции не должны влиять друг на друга. Результаты их работы должны быть такими, как если бы они выполнялись последовательно. Уровни изоляции (Read Uncommitted, Read Committed и др.) определяют степень этой изоляции. Феномен "грязного" чтения (Dirty Read), когда одна транзакция видит незафиксированные изменения другой.
Durability
(Долговечность)
После успешного завершения транзакции (commit) внесенные ею изменения становятся постоянными и сохраняются даже в случае сбоя системы (отключение питания, авария). После подтверждения перевода данные о нем были потеряны из-за сбоя диска.

Практический пример на SQL/JDBC:

Connection conn = dataSource.getConnection();
try {
    conn.setAutoCommit(false); // Начало транзакции (отключение auto-commit)

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

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

    conn.commit(); // ФИКСАЦИЯ. Только здесь изменения становятся видимыми и постоянными.
    System.out.println("Транзакция успешно завершена.");

} catch (SQLException e) {
    conn.rollback(); // ОТКАТ (ROLLBACK) при любой ошибке. Все изменения отменяются (Atomicity).
    System.out.println("Транзакция откачена: " + e.getMessage());
} finally {
    conn.setAutoCommit(true); // Возвращаем режим по умолчанию
    conn.close();
}