SQL

ANSI SQL: FOR UPDATE

dewstream 2025. 8. 1. 08:00
728x90

※ ANSI SQL: FOR UPDATE.

 

안녕하세요. 듀스트림입니다.

 

오늘 포스팅은 동시성 제어를 위한 구문인 FOR UPDATE에 대한 내용입니다.


FOR UPDATE는 동시성 제어(Concurrency Control)를 위한 구문으로
ANSI SQL 표준 기준에서도 정의되어 있습니다.

이 기능은 주로 SELECT 쿼리 결과에 포함된 행들을 업데이트하거나 삭제할 계획이 있을 때,
다른 트랜잭션이 해당 행에 대해 변경하지 못하도록 잠금을 거는 데 사용됩니다.

1. ANSI SQL 표준 기준 문법

SELECT column_list
FROM table_name
[WHERE 조건]
FOR UPDATE [OF column1, column2, ...]
  • FOR UPDATE: 선택한 행들에 행 수준(Row-level) 잠금(Lock)을 겁니다.
  • OF column1, column2, ... (선택 사항): 어떤 컬럼에 대한 업데이트를 의도하는지 명시할 수 있습니다. 이 옵션은 일부 DBMS에서는 무시되거나 의미 없이 동작합니다.

2. 동작 원리

▸ 트랜잭션과 락

  • ANSI SQL에서 FOR UPDATE는 해당 쿼리가 트랜잭션 안에서 실행되어야 하며, 트랜잭션이 커밋되기 전까지 선택된 행들에 대해 다른 트랜잭션의 갱신(Update)이나 삭제(Delete)를 차단합니다.
  • 기본적으로 READ COMMITTED 이상 수준에서 Exclusive Lock(X-lock) 또는 유사한 잠금이 걸립니다.

3. 사용 목적

목적 설명
업데이트/삭제의 안전성 확보 추후 UPDATE/DELETE가 수행될 행을 미리 잠가서, 경쟁 트랜잭션과의 충돌 방지
비즈니스 로직 보호 예: 재고 확인 후 차감할 때, 재고가 동시에 두 트랜잭션에서 차감되는 문제 방지
교착 상태 회피 명시적으로 락을 걸어 예측 가능한 순서로 자원 접근

4. 예제

▸ 단순 FOR UPDATE

BEGIN;

SELECT *
FROM accounts
WHERE account_id = 123
FOR UPDATE;
-- 다른 트랜잭션은 account_id = 123 행에 대해 UPDATE/DELETE 불가

-- 이어서 안전하게 업데이트
UPDATE accounts
SET balance = balance - 100
WHERE account_id = 123;

COMMIT;

 

▸ FOR UPDATE OF

SELECT a.account_id, b.customer_name
FROM accounts a
JOIN customers b ON a.customer_id = b.customer_id
WHERE a.account_id = 123
FOR UPDATE OF a.balance;
-- 이 경우 accounts 테이블의 balance 필드에 대해서만 락을 걸겠다는 의미
  • FOR UPDATE OF 테이블.컬럼: 실제로는 “컬럼 단위 잠금”이 아니라 “해당 테이블의 해당 행(Row)에 락을 건다”는 의미입니다. (컬럼 지정은 락을 걸 대상 테이블을 제한하는 힌트고 실제로는 행 단위 락이 걸립니다.)
  • FOR UPDATE OF의 경우 PostgreSQL, Oracle은 지원하지만 MySQL InnoDB는 지원하지 않습니다.

5. ANSI SQL 표준 격리 수준과의 관계

격리 수준 FOR UPDATE 필요 여부
READ UNCOMMITTED FOR UPDATE 의미 없음 (잠금 안 걸림)
READ COMMITTED FOR UPDATE를 사용하면 명시적으로 잠금 가능
REPEATABLE READ 기본적으로 읽은 행에 Shared Lock이 걸리나, 쓰기 위해선 FOR UPDATE 필요
SERIALIZABLE 가장 엄격한 수준이지만, 명시적 락을 원한다면 여전히 FOR UPDATE 사용 가능

 

+ ANSI 표준과 DBMS별 차이

DBMS FOR UPDATE 지원 여부 특징
PostgreSQL 커서에서도 사용 가능, 조인 시 세부 제어 가능 (FOR UPDATE OF a)
Oracle NOWAIT, SKIP LOCKED 등의 확장 사용 가능
MySQL (InnoDB) 실행 시 인덱스를 통해 레코드 락을 설정
SQL Server FOR UPDATE 대신 WITH (UPDLOCK) 등의 힌트 사용
DB2 표준 방식과 거의 동일하게 동작

 

++ DBMS 확장 옵션 설명

옵션 의미
NOWAIT 락이 이미 잡혀 있으면 대기하지 않고 에러 발생
SKIP LOCKED 락이 걸린 행은 건너뛰고 나머지만 반환
FOR SHARE 읽기 락 (Shared Lock), 다른 트랜잭션이 읽기 가능하지만 쓰기는 차단됨 (PostgreSQL, Oracle 등)

6. 주의 사항

  1. 인덱스가 없다면?
    • 일부 DBMS에서는 테이블 전체 스캔 + 테이블 락처럼 동작할 수 있어 성능 저하 및 교착 상태 위험 증가합니다.
  2. FOR UPDATE 없이도 UPDATE 문에서 락이 걸린다?
    • 맞습니다. 하지만 FOR UPDATE는 읽는 시점부터 의도를 명시해 충돌 가능성을 줄여주는 역할을 합니다.
  3. 조인할 때는 어떤 테이블에 락이 걸릴까?
    • FOR UPDATE OF 테이블별 별칭으로 제어 가능. 그렇지 않으면 대부분의 DBMS는 모든 테이블에 락을 걸거나 제한된 형태로 동작합니다.

 

+ 요약 표

항목 설명
목적 추후 UPDATE/DELETE 예정된 행에 대해 다른 트랜잭션의 접근 차단
작동 방식 트랜잭션 내 SELECT된 행에 행 수준 Exclusive Lock 적용
표준 문법 SELECT ... FOR UPDATE [OF column_list]
사용 조건 트랜잭션 내에서 실행 필요
주의 락으로 인한 데드락, 성능 저하 고려

오늘은 여기까지~

 

728x90