Что делает SQL-команда SELECT FOR UPDATE?

«Что делает SQL-команда SELECT FOR UPDATE?» — вопрос из категории Базы данных, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

SELECT FOR UPDATE — это SQL-команда, которая блокирует выбранные строки для эксклюзивного изменения текущей транзакцией, предотвращая их модификацию другими параллельными транзакциями. Это ключевой механизм для избежания состояния гонки (race condition) при конкурентных операциях чтения-изменения-записи.

Как работает:

  1. Транзакция A выполняет SELECT ... FOR UPDATE.
  2. Выбранные строки блокируются (обычно на уровне строк).
  3. Если транзакция B попытается выполнить SELECT ... FOR UPDATE или UPDATE/DELETE для тех же строк, она будет ожидать завершения транзакции A.
  4. Блокировка снимается при COMMIT или ROLLBACK транзакции A.

Пример использования (Java/JDBC):

Connection conn = dataSource.getConnection();
try {
    conn.setAutoCommit(false); // Начало транзакции

    // 1. Блокируем строку для обновления
    String selectSQL = "SELECT balance FROM accounts WHERE id = ? FOR UPDATE";
    PreparedStatement selectStmt = conn.prepareStatement(selectSQL);
    selectStmt.setInt(1, accountId);
    ResultSet rs = selectStmt.executeQuery();

    // 2. Читаем и изменяем данные
    int newBalance = 0;
    if (rs.next()) {
        newBalance = rs.getInt("balance") + 100;
    }

    // 3. Обновляем заблокированную строку
    String updateSQL = "UPDATE accounts SET balance = ? WHERE id = ?";
    PreparedStatement updateStmt = conn.prepareStatement(updateSQL);
    updateStmt.setInt(1, newBalance);
    updateStmt.setInt(2, accountId);
    updateStmt.executeUpdate();

    conn.commit(); // 4. Фиксируем изменения и снимаем блокировку
} catch (SQLException e) {
    conn.rollback(); // Откат также снимает блокировку
    throw e;
}

Важные замечания:

  • Требует транзакции: Команда имеет смысл только внутри явной транзакции (при отключенном autocommit).
  • Риск взаимоблокировок (deadlock): Возникает, если две транзакции блокируют ресурсы в разном порядке. СУБД обычно обнаруживает и откатывает одну из них.
  • Производительность: Избыточное использование может снизить параллелизм. Используйте только там, где это действительно необходимо для целостности данных.