SQL에서 행 잠그기: SELECT FOR UPDATE 내부
Apr 07, 2025
# SQL
James Reed
Infrastructure Engineer · Leapcell

**SELECT FOR UPDATE
**는 트랜잭션에서 검색된 행을 잠그는 데 사용되는 SQL의 행 레벨 잠금 메커니즘입니다.
이것의 목적은 다른 트랜잭션이 이러한 행을 수정하거나 잠금을 획득하는 것을 방지하는 것이며, 데이터 일관성이 보장되어야 하는 시나리오에서 일반적으로 사용됩니다.
MySQL에서 SELECT FOR UPDATE
의 특정 구현은 스토리지 엔진(예: InnoDB)과 밀접하게 관련되어 있습니다. 핵심적으로는 행 잠금을 사용하여 대상 행을 잠급니다.
핵심 기능
잠금 유형:
SELECT FOR UPDATE
는 쿼리에 의해 검색된 행에 **배타적 잠금 (X 잠금)**을 겁니다.- 다른 트랜잭션은 이러한 행을 수정하거나 공유 또는 배타적 잠금을 걸 수 없습니다.
사용 범위:
- 트랜잭션 내에서 사용해야 합니다 (즉,
BEGIN
과COMMIT
사이). - 트랜잭션을 지원하는 스토리지 엔진(예: InnoDB)을 사용하는 경우에만 효과적입니다.
동작:
- 대상 행이 이미 다른 트랜잭션에 의해 잠겨 있는 경우, 현재 트랜잭션은 잠금이 해제될 때까지 대기 상태가 되거나 시간 초과가 발생합니다.
구현 원리
InnoDB 행 잠금 메커니즘
- InnoDB 스토리지 엔진은 인덱스를 통해 행 레벨 잠금을 구현합니다.
SELECT FOR UPDATE
는 쿼리 조건을 충족하는 모든 행을 잠급니다. 쿼리가 인덱스를 사용하지 않는 경우, 전체 테이블을 잠그는 테이블 잠금으로 저하됩니다.
잠금 프로세스
- 쿼리 실행 중에 InnoDB는 스캔된 각 행에 배타적 잠금을 시도합니다.
- 행이 이미 다른 트랜잭션에 의해 잠겨 있는 경우, 현재 트랜잭션은 해당 잠금이 해제될 때까지 대기합니다.
잠금 유형
- 행 잠금: 검색된 행만 잠그는 인덱스 기반 잠금입니다.
- 갭 잠금:
REPEATABLE READ
격리 수준에서 범위 쿼리가 어떤 행도 적중하지 못하면, 새 행이 삽입되는 것을 방지하기 위해 범위 사이의 갭에 잠금이 걸립니다.
트랜잭션 격리 수준의 효과
- READ COMMITTED: 각 쿼리는 최신 데이터를 읽고 현재 쿼리에서 반환된 행만 잠급니다.
- REPEATABLE READ: 쿼리 결과는 트랜잭션 스냅샷을 기반으로 하며, 잠긴 범위는 일치하지 않는 행을 포함할 수 있습니다 (갭 잠금 때문).
- SERIALIZABLE: 쿼리 범위 내의 모든 데이터를 잠급니다.
실행 과정
다음은 InnoDB를 예로 사용하여 SELECT FOR UPDATE
의 실행 과정을 보여줍니다.
- 트랜잭션 시작:
BEGIN
을 사용하여 트랜잭션을 시작합니다. - 쿼리 및 잠금:
SELECT ... FOR UPDATE
를 실행하여 조건에 맞는 레코드에 배타적 잠금 (X 잠금)을 적용합니다. - 데이터 작업: 잠긴 데이터를 수정하거나 읽습니다.
- 잠금 해제: 트랜잭션이 커밋되면 (
COMMIT
) 잠금이 해제됩니다. 트랜잭션이 롤백되면 (ROLLBACK
) 잠금도 해제됩니다.
예제
기본 사용법
BEGIN; SELECT * FROM orders WHERE order_id = 1 FOR UPDATE; /* order_id = 1인 행을 잠금 */ UPDATE orders SET status = 'processed' WHERE order_id = 1; COMMIT;
다중 트랜잭션 경합 시나리오
트랜잭션 A:
BEGIN; SELECT * FROM orders WHERE order_id = 1 FOR UPDATE; -- order_id = 1인 행을 잠급니다
트랜잭션 B (트랜잭션 A가 커밋되기 전):
BEGIN; SELECT * FROM orders WHERE order_id = 1 FOR UPDATE; -- 트랜잭션 A가 잠금을 해제할 때까지 대기합니다
트랜잭션 B는 트랜잭션 A가 커밋되거나 롤백될 때까지 잠금을 획득할 수 없습니다.
갭 잠금의 동작
REPEATABLE READ
격리 수준에서 범위 쿼리는 갭 잠금을 트리거할 수 있습니다. 예를 들어:
SELECT * FROM orders WHERE order_id BETWEEN 10 AND 20 FOR UPDATE;
쿼리가 결과를 반환하지 않으면 InnoDB는 여전히 10과 20 사이의 범위에 갭 잠금을 걸어 다른 트랜잭션이 이 범위 내에 새 레코드를 삽입하는 것을 방지합니다.
최적화 및 고려 사항
인덱스 사용:
- 인덱스를 사용하는 쿼리는 행 레벨 잠금을 적용하여 테이블 레벨 잠금으로의 저하를 방지합니다.
- 인덱스가 사용되지 않는 경우, 전체 테이블이 잠길 수 있으며, 이는 동시성 성능에 부정적인 영향을 미칩니다.
긴 트랜잭션 피하기:
- 잠금을 너무 오래 유지하면 다른 트랜잭션이 차단될 수 있습니다. 트랜잭션 기간을 최소화하는 것이 좋습니다.
교착 상태 감지:
- InnoDB는 자동으로 교착 상태를 감지하고 트랜잭션 중 하나를 롤백합니다. 교착 상태를 피하기 위해 트랜잭션 로직을 신중하게 설계하는 것이 좋습니다.
비즈니스 요구 사항에 따라 사용:
- 데이터 수정 충돌을 방지하는 데 필요한 경우에만
SELECT FOR UPDATE
를 사용하여 불필요한 잠금 경합을 피하십시오.
요약
SELECT FOR UPDATE
는 행 잠금 메커니즘을 사용하여 동시 수정 충돌을 방지하는 MySQL의 잠금 작업입니다.- 구현은 트랜잭션, 격리 수준 및 인덱스 최적화에 의존합니다.
- 적절하게 사용하면
SELECT FOR UPDATE
는 데이터 일관성을 효과적으로 보호할 수 있지만, 성능 오버헤드에 주의하고 잠금 경합 및 교착 상태를 피해야 합니다.
저희는 백엔드 프로젝트 호스팅을 위한 최고의 선택, Leapcell입니다.
Leapcell은 웹 호스팅, 비동기 작업 및 Redis를 위한 차세대 서버리스 플랫폼입니다.
다국어 지원
- Node.js, Python, Go 또는 Rust로 개발하십시오.
무제한 프로젝트를 무료로 배포
- 사용량에 따라서만 지불하세요. 요청이나 요금이 없습니다.
탁월한 비용 효율성
- 유휴 요금 없이 사용한 만큼 지불합니다.
- 예: $25로 평균 응답 시간 60ms에서 694만 건의 요청을 지원합니다.
간소화된 개발자 경험
- 간편한 설정을 위한 직관적인 UI.
- 완전 자동화된 CI/CD 파이프라인 및 GitOps 통합.
- 실행 가능한 통찰력을 위한 실시간 메트릭 및 로깅.
손쉬운 확장성 및 고성능
- 고도의 동시성을 쉽게 처리할 수 있는 자동 확장.
- 운영 오버헤드가 전혀 없습니다. 구축에만 집중하세요.
문서에서 더 자세히 알아보세요!
X에서 저희를 팔로우하세요: @LeapcellHQ