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

Как ACID влияет на транзакцию

2.0 Middle🔥 111 комментариев
#Базы данных и SQL

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

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

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

ACID и транзакции в БД

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

Что такое ACID

A — Atomicity (Атомарность)
C — Consistency (Согласованность)
I — Isolation (Изоляция)
D — Durability (Надёжность)

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

Определение: Транзакция выполняется полностью или вообще не выполняется (all-or-nothing).

Если что-то пошло не так, все изменения откатываются.

Пример проблемы без Atomicity:

Перевод денег с счета A на счет B:
1. Снять 100 со счёта A          <- успешно
2. Добавить 100 на счет B        <- ОШИБКА СЕТИ!

Результат: деньги потеряны!

С Atomicity:

BEGIN TRANSACTION;
  UPDATE accounts SET balance = balance - 100 WHERE account_id = 'A';
  UPDATE accounts SET balance = balance + 100 WHERE account_id = 'B';
COMMIT;  -- оба изменения применены

-- ИЛИ если ошибка:
ROLLBACK;  -- оба изменения откатаны, как если бы транзакции не было

В Java (Spring):

@Transactional
public void transferMoney(String fromAccount, String toAccount, BigDecimal amount) {
    accountService.withdraw(fromAccount, amount);  // Операция 1
    accountService.deposit(toAccount, amount);     // Операция 2
    // Если любая операция выбросит исключение,
    // обе будут откачены (ROLLBACK)
}

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

Определение: Транзакция переводит БД из одного согласованного состояния в другое.

Все бизнес-правила и ограничения остаются в силе.

Пример проблемы без Consistency:

Ограничение: total_income >= 0 (неотрицательный доход)

Транзакция без контроля:
UPDATE users SET total_income = -100 WHERE user_id = 1;

Результат: нарушено правило!

С Consistency:

-- БД имеет constraint (ограничение)
ALTER TABLE users ADD CONSTRAINT check_income CHECK (total_income >= 0);

-- Попытка нарушить constraint не пройдёт
UPDATE users SET total_income = -100 WHERE user_id = 1;  -- ОШИБКА!

-- Транзакция откатывается, состояние остаётся согласованным

В Java (бизнес-логика):

@Transactional
public void setUserIncome(Long userId, BigDecimal income) {
    if (income.compareTo(BigDecimal.ZERO) < 0) {
        throw new IllegalArgumentException("Доход не может быть отрицательным");
    }
    
    User user = userRepository.findById(userId).orElseThrow();
    user.setTotalIncome(income);
    userRepository.save(user);
    
    // Если исключение, транзакция откатывается
    // БД остаётся в согласованном состоянии
}

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

Определение: Параллельные транзакции не влияют друг на друга до момента коммита.

Каждая транзакция видит консистентный снимок данных.

Пример проблемы без Isolation (Dirty Read):

Транзакция 1:                  Транзакция 2:
1. Прочитать balance = 100
                               2. Прочитать balance = 100
3. balance -= 50
4. Написать balance = 50
                               5. Прочитать balance = 50 (ГРЯЗНОЕ ЧТЕНИЕ!)
                               6. balance += 25
                               7. Написать balance = 75
8. ROLLBACK (ошибка)

Результат: Tx2 работала с откатанными данными!

С Isolation (Serializable):

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

Транзакция 1:                  Транзакция 2:
1. BEGIN
2. SELECT balance (lock)       
3. balance -= 50
4. WRITE balance = 50
5. COMMIT (unlock)
                               6. BEGIN
                               7. SELECT balance = 50 (не грязное!)
                               8. COMMIT

Уровни изоляции в Java:

// Spring JPA
@Transactional(isolation = Isolation.SERIALIZABLE)
public void criticalOperation() {
    // Максимальная защита, но медлено
}

@Transactional(isolation = Isolation.READ_COMMITTED)
public void normalOperation() {
    // Стандартный уровень (по умолчанию)
}

@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void readOnlyFast() {
    // Быстро, но могут быть грязные чтения
}

Проблемы Isolation без контроля:

1. Dirty Read — чтение некоммиченых данных
2. Non-Repeatable Read — одно значение читается по-разному
3. Phantom Read — появляются новые строки между чтениями

Пример Non-Repeatable Read:

Транзакция 1:                  Транзакция 2:
1. SELECT price FROM items 
   WHERE id=5 -> 100
                               2. UPDATE items SET price = 150 WHERE id=5
                               3. COMMIT
4. SELECT price FROM items 
   WHERE id=5 -> 150 (ДРУГОЕ значение!)
5. COMMIT

4. Durability (Надёжность)

Определение: Один раз коммиченые данные остаются в БД даже при сбоях (отключение питания, крах).

Пример проблемы без Durability:

1. Записали данные в оперативную память
2. COMMIT
3. Отключилось питание

Результат: данные потеряны!

С Durability:

1. Записали данные в WAL (Write-Ahead Log) на диск
2. Записали в оперативную память
3. COMMIT (гарантировано на диске)
4. Отключилось питание
5. При перезагрузке БД восстанавливает данные из WAL

Результат: данные сохранены!

В PostgreSQL:

BEGIN TRANSACTION;
  INSERT INTO transactions VALUES (1, 'payment', 100);
COMMIT;  -- Данные синхронно записаны на диск

-- Даже если сервер упадёт через 1 микросекунду,
-- данные будут восстановлены

Как ACID работает вместе

Пример: транзакция перевода денег

@Transactional
public void transferMoney(String from, String to, BigDecimal amount) 
    throws InsufficientFundsException {
    
    // ATOMICITY: либо всё, либо ничего
    User fromUser = userRepository.findById(from);
    
    // CONSISTENCY: проверяем бизнес-правила
    if (fromUser.getBalance().compareTo(amount) < 0) {
        throw new InsufficientFundsException("Недостаточно средств");
    }
    
    // ISOLATION: блокировка для других транзакций
    fromUser.setBalance(fromUser.getBalance().subtract(amount));
    
    User toUser = userRepository.findById(to);
    toUser.setBalance(toUser.getBalance().add(amount));
    
    userRepository.save(fromUser);
    userRepository.save(toUser);
    
    // DURABILITY: после COMMIT данные на диске
}

Что происходит внутри:

  1. ATOMICITY — если исключение на userRepository.save(toUser), обе операции откатываются
  2. CONSISTENCY — проверка баланса защищает инвариант
  3. ISOLATION — другие транзакции не видят промежуточное состояние
  4. DURABILITY — после COMMIT метода, данные гарантированно на диске

Производительность vs ACID

Trade-off между безопасностью и скоростью:

// БЕЗОПАСНО, НО МЕДЛЕННО
@Transactional(isolation = Isolation.SERIALIZABLE)
public void criticalBankOp() { /* ... */ }

// БЫСТРО, НО МЕНЕЕ БЕЗОПАСНО
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void statisticsReport() { /* ... */ }

// БАЛАНС (рекомендуется)
@Transactional(isolation = Isolation.READ_COMMITTED)
public void normalOperation() { /* ... */ }

Практический пример

@Service
public class OrderService {
    
    @Transactional
    public void placeOrder(Long userId, Long productId, int quantity) {
        // ATOMICITY: все шаги или ничего
        User user = userRepository.findByIdForUpdate(userId);
        
        // CONSISTENCY: проверяем правила
        if (user.getBalance() < getPrice(productId) * quantity) {
            throw new PaymentFailedException("Недостаточно средств");
        }
        
        Product product = productRepository.findByIdForUpdate(productId);
        
        if (product.getStock() < quantity) {
            throw new OutOfStockException("Товара нет в наличии");
        }
        
        // ISOLATION: блокировка строк
        Order order = new Order(user, product, quantity);
        orderRepository.save(order);
        
        user.setBalance(user.getBalance() - calculateTotal(product, quantity));
        product.setStock(product.getStock() - quantity);
        
        userRepository.save(user);
        productRepository.save(product);
        
        // DURABILITY: данные на диске после метода
    }
}

Итог

  1. Atomicity — транзакция целиком или ничего
  2. Consistency — БД остаётся в согласованном состоянии
  3. Isolation — транзакции не влияют друг на друга
  4. Durability — коммиченые данные сохраняются

Bez ACID могут быть потеря денег, дублирование, инкосинстентность. ACID — основа надёжных систем, особенно финансовых.

Как ACID влияет на транзакцию | PrepBro