프로그래밍/MSSQL

[MSSQL] 트랜잭션 락 (Transaction Locking) 이슈 완벽 정리

KRLAND 2025. 3. 26. 17:37

🔐 MSSQL 트랜잭션 락 (Locking) 이슈 완벽 정리


✅ 1. 락이란?

**락(Lock)**은 데이터의 정합성과 일관성을 보장하기 위해
SQL Server가 데이터에 대한 접근을 제어하는 메커니즘입니다.

📦 락의 주요 유형

락 종류 설명
Shared (S) 읽기 락 (SELECT 시 걸림)
Exclusive (X) 쓰기 락 (INSERT, UPDATE, DELETE)
Update (U) 업데이트 시 걸리는 임시 락
Intent (IS, IX) 테이블/페이지 단위에 걸리는 의도 락

🔥 2. 락이 걸리는 주요 상황

상황 설명
SELECT + 트랜잭션 읽은 데이터가 락 걸려 다른 트랜잭션에서 못 씀
UPDATE / DELETE 대기 다른 트랜잭션이 아직 COMMIT 안 해서 기다림
인덱스 미사용 풀스캔 → 더 넓은 범위 락 발생
같은 테이블을 동시에 수정 레코드 충돌 발생
오래 지속되는 트랜잭션 LOCK 보유 시간 증가 → BLOCKING 확률 증가

⚔ 3. "락이 걸리는 현상"의 유형

🟡 Blocking (블로킹)

  • 한 트랜잭션이 락을 잡고 있고, 다른 트랜잭션이 기다리는 상태
  • 성능 저하, 대기 증가

🔴 Deadlock (교착 상태)

  • 서로의 락을 기다리다가 순환 대기가 되어 둘 다 멈추는 상태
  • SQL Server가 자동으로 한 쪽 트랜잭션을 강제 종료함

🛡 4. 락 회피 전략 (총망라)

✅ A. 트랜잭션을 짧고 빠르게

  • 트랜잭션은 꼭 필요한 최소한의 범위로만 잡기
-- ❌ 안 좋은 예: SELECT 포함
BEGIN TRANSACTION
SELECT * FROM Orders WHERE UserID = @UserID;
UPDATE Orders SET Status = 'C' WHERE OrderID = 1;
COMMIT;

-- ✅ 좋은 예: SELECT는 트랜잭션 밖에서 처리
SELECT * FROM Orders WHERE UserID = @UserID;

BEGIN TRANSACTION
UPDATE Orders SET Status = 'C' WHERE OrderID = 1;
COMMIT;

 


✅ B. 일관된 접근 순서로 DEADLOCK 회피

  • 여러 테이블에 동시에 접근한다면 항상 같은 순서로
-- ❌ 서로 다른 순서로 접근하면 데드락 가능
T1: UPDATE A → UPDATE B  
T2: UPDATE B → UPDATE A

-- ✅ 항상 같은 순서
T1, T2: UPDATE A → UPDATE B

 


✅ C. 적절한 인덱스를 사용해서 락 범위 최소화

  • 인덱스 없으면 테이블 전체 스캔 → 넓은 범위 락 발생
  • WHERE 조건과 JOIN 조건에 인덱스를 걸어주면 효과적

✅ D. WITH (NOLOCK) 사용 (주의 요망)

SELECT * FROM Orders WITH (NOLOCK);

 

  • S 락을 잡지 않음 → 다른 트랜잭션에 영향을 주지 않음
  • 대신 더티 리드 (읽은 값이 COMMIT되지 않았을 수 있음) 위험 존재
  • 조회 전용, 중요하지 않은 SELECT에만 사용

✅ E. READ COMMITTED SNAPSHOT 설정 (추천)

  • 버전 기반 읽기 기능 → SELECT가 다른 트랜잭션에 의해 블로킹되지 않음
-- 데이터베이스에 설정
ALTER DATABASE [YourDB] SET READ_COMMITTED_SNAPSHOT ON;

 

  • 즉, SELECT는 버전 데이터를 읽고, WRITE는 별도로 진행됨
  • SELECT로 인한 락을 근본적으로 회피할 수 있음

✅ F. WAIT 옵션 조정 (SET LOCK_TIMEOUT)

SET LOCK_TIMEOUT 5000;  -- 5초 대기 후 오류 반환
  • 무한정 기다리지 않고 일정 시간 지나면 오류 반환
  • 프로그램에서 리트라이 로직과 함께 쓰면 유용

✅ G. 트랜잭션 격리 수준 변경

수준 설명 특징
READ UNCOMMITTED NOLOCK과 같음 가장 위험, 거의 사용 금지
READ COMMITTED (기본) COMMIT된 데이터만 읽음 블로킹 발생 가능
SNAPSHOT 버전 기반 읽기 RCSI와 유사
SERIALIZABLE 완전 고립 락 많이 생김, 피할 것
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;

 


🧪 5. 실시간 락/블로킹 감지 쿼리

현재 블로킹 상태 조회

SELECT 
    blocking_session_id, session_id, wait_type, wait_time, wait_resource
FROM sys.dm_exec_requests
WHERE blocking_session_id <> 0;

 

데드락 발생 정보 로그 확인 (XEvent)

-- 데드락 Extended Event 활성화
CREATE EVENT SESSION DeadlockSession
ON SERVER
ADD EVENT sqlserver.deadlock_graph
ADD TARGET package0.ring_buffer
WITH (STARTUP_STATE = ON);

🧠 실무 정리 요약

 

전략 설명
트랜잭션 최소화 SELECT는 바깥, UPDATE만 안에서
동일한 테이블 접근 순서 유지 데드락 예방
인덱스 튜닝 락 범위 최소화
SNAPSHOT 격리 or RCSI 사용 읽기 락 해소
SELECT에 NOLOCK 조건부 사용 더티 리드 주의
LOCK_TIMEOUT 설정 블로킹 무한대기 방지
정기적인 모니터링 DM 뷰, XEvent 등 활용

📌 결론

트랜잭션 락은 반드시 발생하지만, 잘 설계하면 거의 대부분 예방 또는 완화할 수 있습니다.
중요한 건:

  • 최소 트랜잭션 범위
  • 일관된 처리 순서
  • 데이터베이스 격리 전략
  • 읽기/쓰기 분리

입니다.