Sobes.tech
Senior

PostgreSQL vəzifəsi: 10 eyni vaxtda çıxarış sorğusu ilə balansın ardıcıllığını təmin etmək və mənfi balansdan qorunmaq

sobes.tech Süni İntellekt

AI-dan cavab

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