반복 가능 읽기 격리에서 SELECT FOR UPDATE 사용

이 페이지에서는 반복 가능한 읽기 격리에서 FOR UPDATE 절을 사용하는 방법을 설명합니다.

FOR UPDATE 절의 잠금 메커니즘은 반복 가능한 읽기와 직렬화 가능한 격리에서 서로 다릅니다. 직렬화 가능 격리와 달리 FOR UPDATE 절은 반복 가능 읽기 격리에서 잠금을 획득하지 않습니다. FOR UPDATE의 잠금에 대한 자세한 내용은 직렬화 가능한 격리에서 SELECT FOR UPDATE 사용을 참고하세요.

FOR UPDATE 절을 사용하는 방법을 알아보려면 GoogleSQLPostgreSQL FOR UPDATE 참조 가이드를 참고하세요.

FOR UPDATE 절을 사용하는 이유

트랜잭션이 반복 가능한 읽기 격리로 실행되면 SELECT 문으로 쿼리된 데이터는 항상 트랜잭션에 대해 설정된 스냅샷 타임스탬프에서 반환됩니다. 그런 다음 트랜잭션이 쿼리된 데이터를 기반으로 업데이트를 실행하는 경우 동시 트랜잭션도 쿼리된 데이터를 업데이트하면 정확성 문제가 발생할 수 있습니다. 자세한 내용은 읽기-쓰기 충돌 및 정확성을 참고하세요.

SELECT 문으로 쿼리된 데이터가 트랜잭션이 커밋될 때도 유효하도록 하려면 반복 가능한 읽기 격리와 함께 FOR UPDATE 절을 사용하면 됩니다. FOR UPDATE를 사용하면 읽기-쓰기 충돌에도 불구하고 트랜잭션 정확성이 보장됩니다. 읽기와 수정 사이에 다른 트랜잭션에 의해 데이터가 수정되었을 수 있습니다.

쿼리 구문

이 섹션에서는 FOR UPDATE 절을 사용할 때의 쿼리 구문을 안내합니다.

가장 일반적인 사용법은 최상위 SELECT 문에 있습니다. 예를 들면 다음과 같습니다.

SELECT SingerId, SingerInfo
FROM Singers WHERE SingerID = 5
FOR UPDATE;

FOR UPDATE 절은 트랜잭션이 커밋될 때 SELECT 문과 SingerID = 5로 쿼리된 데이터가 여전히 유효하도록 보장하여 동시 트랜잭션이 쿼리된 데이터를 업데이트할 경우 발생할 수 있는 정확성 문제를 방지합니다.

WITH 문에서 사용

WITH 문에 대한 외부 수준 쿼리에서 FOR UPDATE를 지정하면 FOR UPDATE 절은 WITH 문 내에서 검색된 범위를 확인하지 않습니다.

다음 쿼리에서는 FOR UPDATE가 공통 테이블 표현식 (CTE) 쿼리로 전파되지 않으므로 스캔된 범위가 검증되지 않습니다.

WITH s AS (SELECT SingerId, SingerInfo FROM Singers WHERE SingerID > 5)
SELECT * FROM s
FOR UPDATE;

CTE 쿼리에 FOR UPDATE 절이 지정되면 CTE 쿼리의 스캔된 범위가 검증됩니다.

다음 예시에서는 SingerId > 5가 유효성 검사된 행의 SingerIdSingerInfo 셀입니다.

WITH s AS
  (SELECT SingerId, SingerInfo FROM Singers WHERE SingerId > 5 FOR UPDATE)
SELECT * FROM s;

서브 쿼리에서 사용

하나 이상의 하위 쿼리가 있는 외부 수준 쿼리에서 FOR UPDATE 절을 사용할 수 있습니다. 최상위 쿼리와 하위 쿼리 내에서 스캔된 범위는 표현식 하위 쿼리를 제외하고 검증됩니다.

다음 쿼리는 SingerId > 5.에 대해 SingerIdSingerInfo 셀을 검증합니다.

(SELECT SingerId, SingerInfo FROM Singers WHERE SingerId > 5) AS t
FOR UPDATE;

다음 쿼리는 표현식 하위 쿼리 내에 있으므로 Albums 테이블의 셀을 검증하지 않습니다. 표현식 하위 쿼리에서 반환된 행의 SingerIdSingerInfo 셀이 검증됩니다.

SELECT SingerId, SingerInfo
FROM Singers
WHERE SingerId = (SELECT SingerId FROM Albums WHERE MarketingBudget > 100000)
FOR UPDATE;

뷰를 쿼리하는 데 사용

다음 예시와 같이 FOR UPDATE 절을 사용하여 뷰를 쿼리할 수 있습니다.

CREATE VIEW SingerBio AS SELECT SingerId, FullName, SingerInfo FROM Singers;

SELECT * FROM SingerBio WHERE SingerId = 5 FOR UPDATE;

뷰를 정의할 때 FOR UPDATE 절을 사용할 수 없습니다.

지원되지 않는 사용 사례

다음 FOR UPDATE 사용 사례는 지원되지 않습니다.

  • Spanner 외부에서 코드를 실행하기 위한 상호 제외 메커니즘: Spanner 외부 리소스에 대한 독점 액세스를 보장하는 데 Spanner의 잠금을 사용하지 마세요. Spanner에 의해 트랜잭션이 중단될 수 있습니다. 예를 들어 애플리케이션 코드에서 명시적으로 또는 Spanner JDBC 드라이버와 같은 클라이언트 코드에서 암시적으로 트랜잭션이 재시도되는 경우 커밋된 시도 중에만 잠금이 보장됩니다.
  • LOCK_SCANNED_RANGES 힌트와 함께 사용: 동일한 쿼리에서 FOR UPDATE 절과 LOCK_SCANNED_RANGES 힌트를 모두 사용할 수는 없으며, 그러지 않으면 Spanner에서 오류를 반환합니다. 자세한 내용은 LOCK_SCANNED_RANGES 힌트와의 비교를 참고하세요.
  • 전체 텍스트 검색 쿼리: 전체 텍스트 검색 색인을 사용하는 쿼리에서는 FOR UPDATE 절을 사용할 수 없습니다.
  • 읽기 전용 트랜잭션: FOR UPDATE 절은 읽기-쓰기 트랜잭션 내에서 실행되는 쿼리에서만 유효합니다.
  • DDL 문 내: 나중에 실행하기 위해 저장된 DDL 문 내의 쿼리에서는 FOR UPDATE 절을 사용할 수 없습니다. 예를 들어 뷰를 정의할 때 FOR UPDATE 절을 사용할 수 없습니다.

다음 단계