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. 주의 사항
- 인덱스가 없다면?
- 일부 DBMS에서는 테이블 전체 스캔 + 테이블 락처럼 동작할 수 있어 성능 저하 및 교착 상태 위험 증가합니다.
- FOR UPDATE 없이도 UPDATE 문에서 락이 걸린다?
- 맞습니다. 하지만 FOR UPDATE는 읽는 시점부터 의도를 명시해 충돌 가능성을 줄여주는 역할을 합니다.
- 조인할 때는 어떤 테이블에 락이 걸릴까?
- FOR UPDATE OF 테이블별 별칭으로 제어 가능. 그렇지 않으면 대부분의 DBMS는 모든 테이블에 락을 걸거나 제한된 형태로 동작합니다.
+ 요약 표
| 항목 | 설명 |
| 목적 | 추후 UPDATE/DELETE 예정된 행에 대해 다른 트랜잭션의 접근 차단 |
| 작동 방식 | 트랜잭션 내 SELECT된 행에 행 수준 Exclusive Lock 적용 |
| 표준 문법 | SELECT ... FOR UPDATE [OF column_list] |
| 사용 조건 | 트랜잭션 내에서 실행 필요 |
| 주의 | 락으로 인한 데드락, 성능 저하 고려 |
오늘은 여기까지~
728x90