Sobes.tech
Назад к вопросам
Senior
3

Задача по PostgreSQL: обеспечить консистентность баланса и защиту от отрицательного баланса при 10 одновременных запросах на списание

Компании, где спрашивали
РТК ИТ

Ответ от нейросети

sobes.tech AI

Для обеспечения консистентности баланса и защиты от отрицательного баланса при одновременных списаниях в PostgreSQL можно использовать транзакции с блокировкой строк и проверкой баланса внутри транзакции.

Пример подхода:

  1. Начинаем транзакцию.
  2. Выполняем SELECT ... FOR UPDATE по нужной записи баланса, чтобы заблокировать строку для других транзакций.
  3. Проверяем, что баланс достаточен для списания.
  4. Если достаточно, обновляем баланс.
  5. Фиксируем транзакцию.

Это гарантирует, что параллельные запросы будут последовательно обрабатывать обновление баланса, не допуская отрицательного значения.

Пример SQL внутри Java (JDBC):

connection.setAutoCommit(false);
try (PreparedStatement psSelect = connection.prepareStatement(
        "SELECT balance FROM accounts WHERE id = ? FOR UPDATE")) {
    psSelect.setInt(1, accountId);
    ResultSet rs = psSelect.executeQuery();
    if (!rs.next()) {
        throw new RuntimeException("Account not found");
    }
    BigDecimal balance = rs.getBigDecimal("balance");
    if (balance.compareTo(amountToWithdraw) < 0) {
        throw new RuntimeException("Insufficient funds");
    }
    try (PreparedStatement psUpdate = connection.prepareStatement(
            "UPDATE accounts SET balance = balance - ? WHERE id = ?")) {
        psUpdate.setBigDecimal(1, amountToWithdraw);
        psUpdate.setInt(2, accountId);
        psUpdate.executeUpdate();
    }
    connection.commit();
} catch (Exception e) {
    connection.rollback();
    throw e;
} finally {
    connection.setAutoCommit(true);
}

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