1 분 소요

MSSQL에서 프로시저 맨 앞에 다음 문장을 넣으면, 그 안의 모든 SELECT에 WITH(NOLOCK)을 붙인 것과 같은 효과가 난다.

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

2026년 수정 안내 원래 이 글은 “SELECT 하는 프로시저라면 이 문장을 넣는다”는 한 줄짜리 규칙으로 적혀 있었다. 조건 없이 권할 만한 설정이 아니라서, 실제로 무슨 일이 일어나는지와 어떤 경우에 써도 되는지를 정리해 다시 쓴다.

무엇을 하는 설정인가

READ UNCOMMITTED공유 락을 걸지 않고 읽는다. 그래서 다른 트랜잭션이 쓰고 있는 행을 기다리지 않고 그대로 통과한다. 조회가 차단되지 않으니 빨라 보이고, 조회 때문에 쓰기가 막히는 일도 없어진다.

여기까지가 흔히 알려진 장점이고, 대가가 따라온다.

무엇을 감수해야 하는가

1. Dirty read — 커밋되지 않은 데이터를 읽는다

다른 트랜잭션이 아직 커밋하지 않은 값을 읽는다. 그 트랜잭션이 롤백되면, 내가 읽은 값은 한 번도 존재한 적 없는 데이터가 된다.

2. 행이 누락되거나 중복된다 — 이쪽이 훨씬 덜 알려져 있다

이게 진짜 무서운 부분이다. 스캔 도중 다른 세션이 페이지 분할(page split)을 일으키면, 이미 지나간 위치로 행이 옮겨가 아예 읽히지 않거나, 아직 안 지나간 위치로 옮겨와 같은 행을 두 번 읽는다.

커밋이 완료된 멀쩡한 데이터인데도 그렇다. COUNT(*)가 틀리게 나오는 원인이 대개 이것이다.

3. 실행 중 오류가 나기도 한다

읽는 도중 페이지 구조가 바뀌면 Error 601: Could not continue scan with NOLOCK due to data movement가 발생할 수 있다.

그래서 언제 쓰는가

써도 되는 경우

  • 대략적인 수치만 필요한 모니터링·대시보드 조회
  • 이미 갱신이 끝난 과거 데이터만 보는 통계·리포트
  • 어차피 다음 순간 값이 바뀌는 실시간 현황판

쓰면 안 되는 경우

  • 재화, 아이템, 결제, 정산 등 값이 틀리면 사고가 나는 모든 조회
  • 읽은 값을 근거로 다시 쓰기를 하는 경우 (SELECT 후 그 값으로 UPDATE)
  • 정확한 건수가 필요한 집계

게임 서버라면 대부분의 조회가 두 번째 범주에 들어간다. 그래서 “SELECT 프로시저에는 일단 넣는다”는 규칙은 위험하다.

더 나은 선택지: RCSI

읽기가 쓰기에 막히는 게 문제라면, 답은 READ UNCOMMITTED가 아니라 RCSI(Read Committed Snapshot Isolation) 다.

ALTER DATABASE [GameDB] SET READ_COMMITTED_SNAPSHOT ON

행 버전을 이용해 커밋된 시점의 일관된 스냅샷을 읽는다. 읽기가 쓰기를 막지 않고 쓰기도 읽기를 막지 않으면서, dirty read나 행 누락은 발생하지 않는다.

대가는 tempdb 사용량 증가와 행마다 14바이트의 버전 정보다. 대부분의 경우 이 비용을 치를 값어치가 있다.

참고자료

댓글 남기기