← Назад к вопросам

Назови аббревиатуру требований к транзакциям

1.3 Junior🔥 151 комментариев
#Docker, Kubernetes и DevOps

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

ACID: основные требования к транзакциям

ACID — это аббревиатура четырёх фундаментальных требований к транзакциям в базах данных. Это один из самых важных концептов для любого разработчика.

Определение ACID

ACID расшифровывается как:

  1. A — Atomicity (Атомарность)
  2. C — Consistency (Согласованность)
  3. I — Isolation (Изоляция)
  4. D — Durability (Долговечность)

1. Atomicity (Атомарность)

Определение: Транзакция либо полностью выполнится, либо вообще не выполнится. Нет частичного выполнения.

// Пример: перевод денег между счётами
@Transactional
public void transferMoney(int fromAccountId, int toAccountId, BigDecimal amount) {
    // Шаг 1: снять со счёта
    Account fromAccount = accountRepo.findById(fromAccountId).orElseThrow();
    fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
    accountRepo.save(fromAccount);
    
    // Шаг 2: положить на счёт
    Account toAccount = accountRepo.findById(toAccountId).orElseThrow();
    toAccount.setBalance(toAccount.getBalance().add(amount));
    accountRepo.save(toAccount);
    
    // ✅ ATOMICITY: если произойдёт ошибка на шаге 2,
    // вся транзакция откатится (шаг 1 отменится тоже)
}

// Неправильно (нарушает Atomicity):
public void transferMoneyBad(int fromAccountId, int toAccountId, BigDecimal amount) {
    // Шаг 1: снять со счёта
    Account fromAccount = accountRepo.findById(fromAccountId).orElseThrow();
    fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
    accountRepo.save(fromAccount);  // БД записана
    
    // Ошибка: счёт не найден
    Account toAccount = accountRepo.findById(-1).orElseThrow();  // Exception!
    // ❌ Деньги снялись, но не положились никуда!
}

Механизм в БД:

  • Все операции в транзакции выполняются как одно целое
  • При ошибке делается rollback (откат) всех изменений
  • Используется журнал транзакций (transaction log) для отката

2. Consistency (Согласованность)

Определение: База данных переходит из одного согласованного состояния в другое согласованное состояние. Все правила целостности и ограничения соблюдаются.

// Пример: правила целостности
@Entity
public class Account {
    @Id
    private Integer id;
    
    @Column(nullable = false)  // ✅ Ограничение: не может быть NULL
    private String accountNumber;
    
    @Column(nullable = false)
    private BigDecimal balance;
    
    @Min(0)  // ✅ Ограничение: не может быть отрицательный баланс
    public BigDecimal getBalance() {
        return balance;
    }
}

// Проверка консистентности перед сохранением
@Transactional
public void transferMoney(int fromId, int toId, BigDecimal amount) {
    // Начальное состояние: консистентно
    // Все балансы >= 0, все счета имеют номер
    
    Account from = accountRepo.findById(fromId).orElseThrow();
    
    // ✅ Проверяем: можем ли снять?
    if (from.getBalance().compareTo(amount) < 0) {
        throw new InsufficientFundsException();  // Откатываем
    }
    
    from.setBalance(from.getBalance().subtract(amount));
    Account to = accountRepo.findById(toId).orElseThrow();
    to.setBalance(to.getBalance().add(amount));
    
    // ✅ Все ограничения соблюдены, переходим в новое консистентное состояние
}

// ❌ Нарушение consistenct:
Account account = new Account();
account.setBalance(new BigDecimal("-100"));  // Отрицательный баланс!
account.setAccountNumber(null);  // NULL!
acountRepo.save(account);  // БД выкинет ошибку

Правила консистентности:

  • Constraints: NOT NULL, UNIQUE, PRIMARY KEY, FOREIGN KEY
  • Triggers: автоматические проверки при изменении
  • Бизнес-правила: логика приложения

3. Isolation (Изоляция)

Определение: Параллельно выполняющиеся транзакции не видят незавершённые изменения друг друга. Каждая транзакция выполняется так, будто других транзакций нет.

// Пример: две параллельные транзакции

// Thread 1 (Transaction 1)          // Thread 2 (Transaction 2)
@Transactional                       @Transactional
public void transaction1() {         public void transaction2() {
    Account acc = findById(1);       Account acc = findById(1);
    // balance = 1000                // ✅ Видит исходное значение 1000
    
    acc.setBalance(900);             acc.setBalance(500);
    Thread.sleep(1000);              // ждёт
    // Еще не commit            
    
    save(acc);                       // Какое значение будет?
    commit();  // Commit             // 500 или 900?
}                                    
                                     save(acc);
                                     commit();
}

Уровни изоляции (от слабого к сильному):

@Transactional(isolation = Isolation.READ_UNCOMMITTED)
// ❌ Читает грязные данные (незавершённые изменения других транзакций)

@Transactional(isolation = Isolation.READ_COMMITTED)
// ✅ Стандартный уровень - читает только завершённые изменения
// Проблемы: non-repeatable read, phantom reads

@Transactional(isolation = Isolation.REPEATABLE_READ)
// ✅ Гарантирует: прочитанные строки не изменятся
// Проблемы: phantom reads (новые строки могут появиться)

@Transactional(isolation = Isolation.SERIALIZABLE)
// ✅ Самый сильный уровень - полная изоляция
// Транзакции выполняются как последовательно (одна за другой)
// Медленно, но максимально безопасно

Проблема: Dirty Read (грязное чтение)

// Thread 1 (READ_UNCOMMITTED)       // Thread 2
@Transactional                       @Transactional
public void tr1() {                  public void tr2() {
    Account acc = findById(1);           Account acc = findById(1);
    // balance = 1000                    
    acc.setBalance(2000);                
    save(acc);                           balance = acc.getBalance();
    // Ещё не commit                     // balance = 2000 ❌ Грязные данные!
    
    // Ошибка!                           // Thread 2 видит незавершённые изменения
    throw new Exception();               
    // rollback - balance вернулась 1000  
}                                      // Но Thread 2 уже использовал 2000!

4. Durability (Долговечность)

Определение: Когда транзакция закоммичена, данные сохраняются навсегда и не потеряются даже при отказе системы.

@Transactional
public void transferMoney(int from, int to, BigDecimal amount) {
    // ... операции ...
    save(...);
    // ✅ После commit() данные гарантированно сохранены на диск
    // Даже если сейчас отключится электричество, данные останутся!
}

// Механизм Durability:
// 1. Write-Ahead Logging (WAL)
//    - Сначала пишем все изменения в log на диск
//    - Потом применяем изменения в памяти
//    - При сбое восстанавливаем из лога

// 2. Fsync и синхронизация
//    - После commit() БД вызывает fsync()
//    - Это гарантирует, что данные на диске

// 3. Репликация
//    - Копируем данные на другие сервера
//    - Если один сервер отказал, данные есть на других

Пример потери данных БЕЗ Durability:

// ❌ Без fsync (или с отключённым)
public void save(User user) {
    // Данные в памяти (буфер ОС)
    buffer.write(user);
    // Нет fsync() -> нет гарантии на диске
    
    // Отключилось электричество
    // -> данные потеряны!
}

// ✅ С fsync (Durability)
@Transactional
public void save(User user) {
    buffer.write(user);
    fsync();  // Записать буфер на диск
    // Теперь данные гарантированно на диске
}

ACID в контексте Java/Spring

@Service
public class BankService {
    
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public void transferMoney(TransferRequest request) {
        // Atomicity: весь метод - одна транзакция
        // Если ошибка - всё откатится
        
        // Isolation: другие транзакции не видят промежуточные состояния
        Account from = accountRepo.findById(request.getFromId()).orElseThrow();
        
        // Consistency: проверяем все ограничения
        if (from.getBalance().compareTo(request.getAmount()) < 0) {
            throw new InsufficientFundsException();
        }
        
        // ... операции ...
        
        // Durability: при завершении без ошибок
        // данные гарантированно сохранены на диск
    }
}

Не все БД одинаково поддерживают ACID

// ✅ PostgreSQL - полная поддержка ACID
// ✅ MySQL (InnoDB) - полная поддержка ACID
// ✅ Oracle - полная поддержка ACID
// ⚠️ MongoDB - ACID с ограничениями (multi-document транзакции с v4.0)
// ❌ Cassandra - НЕ поддерживает ACID (eventual consistency)
// ❌ Redis - НЕ гарантирует Atomicity между командами

Компромиссы в распределённых системах

BASE модель (альтернатива ACID):

// В распределённых системах часто выбирают BASE вместо ACID
// Basically Available
// Soft state
// Eventual consistency

// Пример: микросервисная архитектура
public void transferMoneyDistributed(int from, int to, BigDecimal amount) {
    // Запрос к сервису A: снять деньги
    // Запрос к сервису B: положить деньги
    // Если оба успешны - отлично
    // Если B неудачен - нужна компенсация (откат снятия из A)
    
    // ACID гарантирует это автоматически
    // В распределённой системе - нужно делать вручную (Saga pattern)
}

Практические выводы

Atomicity — всё или ничего ✅ Consistency — соблюдение всех правил ✅ Isolation — транзакции не мешают друг другу ✅ Durability — данные сохранены навсегда ✅ @Transactional в Spring — гарантирует ACID (если БД поддерживает) ⚠️ Распределённые системы — нужны дополнительные паттерны (Saga, eventual consistency)

Ответ на интервью: "ACID — это аббревиатура четырёх требований к транзакциям: Atomicity (транзакция либо полностью выполнится, либо вообще не выполнится), Consistency (база переходит из одного согласованного состояния в другое), Isolation (параллельные транзакции не видят друг друга промежуточные состояния) и Durability (после коммита данные гарантированно сохранены). Эти свойства гарантируют надёжность и безопасность работы с данными в базах данных."

Назови аббревиатуру требований к транзакциям | PrepBro