Sobes.tech
Senior

Tarea de PostgreSQL: garantizar la consistencia del saldo y protección contra saldo negativo con 10 solicitudes simultáneas de retiro

sobes.tech AI

Respuesta de la 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 и более одновременных запросах.