Sobes.tech
Senior

Tâche PostgreSQL : assurer la cohérence du solde et la protection contre le solde négatif avec 10 requêtes simultanées de retrait

sobes.tech IA

Réponse de l'IA

Для обеспечения консистентности баланса и защиты от отрицательного баланса при одновременных списаниях в 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 и более одновременных запросах.