Работали ли вы с транзакциями в базах данных?

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

Ответ

Да, постоянно. Транзакции — фундаментальный механизм для обеспечения целостности данных, особенно в системах, где важна согласованность (финансы, инвентаризация, заказы).

Ключевые принципы (ACID), которые я реализую на практике:

  • Atomicity (Атомарность): Все операции в транзакции выполняются как единое целое.
  • Consistency (Согласованность): Транзакция переводит БД из одного валидного состояния в другое.
  • Isolation (Изолированность): Параллельные транзакции не мешают друг другу.
  • Durability (Долговечность): Результаты зафиксированной транзакции сохраняются навсегда.

Пример на SQL (PostgreSQL/MySQL):

BEGIN; -- Начало транзакции

UPDATE accounts SET balance = balance - 100.00 WHERE user_id = 123; -- Списание
UPDATE accounts SET balance = balance + 100.00 WHERE user_id = 456; -- Зачисление
-- Здесь может быть сложная бизнес-логика, проверки...

COMMIT; -- Подтверждение, если все успешно
-- или
ROLLBACK; -- Откат в случае ошибки (например, если сумма списания > баланса)

Работа в коде приложения (на примере Java/Spring):

@Service
public class TransferService {
    @Transactional // Аннотация Spring, управляющая границами транзакции
    public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
        Account from = accountRepository.findById(fromId).orElseThrow();
        Account to = accountRepository.findById(toId).orElseThrow();

        if (from.getBalance().compareTo(amount) < 0) {
            throw new InsufficientFundsException(); // Вызовет автоматический rollback
        }

        from.debit(amount);
        to.credit(amount);

        accountRepository.save(from);
        accountRepository.save(to);
        // При успешном завершении метода Spring автоматически выполнит commit
    }
}

Важные аспекты, которые я учитываю:

  • Уровни изоляции: Выбираю подходящий уровень (Read Committed, Repeatable Read, Serializable) в зависимости от требований к согласованности и производительности.
  • Длинные транзакции: Стараюсь избегать, так как они блокируют ресурсы и ухудшают производительность. Разбиваю сложные операции или использую компенсирующие транзакции (Saga-паттерн) в микросервисной архитектуре.
  • Распределенные транзакции: В микросервисах классические 2PC (two-phase commit) используются редко. Вместо этого применяю паттерны Saga или иду на eventual consistency с использованием событий.