SQL Server의 READ_COMMITTED_SNAPSHOT(RCSI)이 ON이면 READ COMMITTED SELECT는 공유 데이터 잠금 대신 문장 시작 시점에 커밋된 행 버전으로 일관된 결과를 읽습니다. OFF이면 일반적으로 공유 잠금을 사용합니다.

RCSI의 읽기 기준은 문장 단위입니다. 여러 SELECT가 같은 트랜잭션에 있다는 이유만으로 하나의 트랜잭션 시작 스냅샷을 공유하지는 않습니다.

RCSI는 READ COMMITTED 읽기의 구현을 바꿉니다

SQL Server의 READ COMMITTED 격리수준은 커밋되지 않은 값을 읽지 못하게 합니다. 하지만 그 읽기를 공유 잠금으로 보호할지 행 버전으로 제공할지는 데이터베이스의 READ_COMMITTED_SNAPSHOT 옵션에 따라 달라집니다. SQL Server 2022의 일반 기본값은 OFF입니다. OFF이면 읽는 동안 공유 잠금을 사용하고, ON이면 SELECT 문장 시작 시점에 커밋된 데이터 버전으로 결과를 구성합니다.

RCSI는 READ COMMITTED의 이름을 다른 격리수준으로 바꾸는 설정이 아닙니다. READ COMMITTED를 사용하는 문장이 어떤 읽기 방식을 택할지 데이터베이스 수준에서 바꿉니다. Microsoft는 Azure SQL Database 등 일부 제품에서는 ON이 기본일 수 있다고 별도로 안내합니다. 따라서 'SQL Server의 기본값'을 말할 때 제품을 명시해야 하며, 한 제품의 설정을 다른 호스팅 제품에 그대로 적용하면 안 됩니다.

같은 트랜잭션의 두 SELECT도 서로 다른 문장 시점을 볼 수 있습니다

교육용 예시로 재고가 처음에 10개라고 가정합니다. A는 READ COMMITTED 트랜잭션 안에서 첫 SELECT를 실행합니다. 그 사이 B가 재고를 8로 바꾸어 커밋한 뒤, A가 두 번째 SELECT를 새로 실행합니다. RCSI OFF에서는 첫 읽기 도중 공유 잠금이 다른 쓰기를 기다리게 할 수 있고, 문장이 끝난 뒤 B가 커밋하면 두 번째 SELECT는 새 값을 읽을 수 있습니다. RCSI ON에서도 두 번째 문장은 시작 시점에 커밋된 8을 볼 수 있습니다. ON이 트랜잭션 시작 시점의 값 10을 끝까지 고정하는 것은 아닙니다.

READ COMMITTED에서 옵션별로 SELECT가 읽는 기준
설정첫 SELECT 시작B가 8을 커밋한 뒤 A의 다음 SELECT읽기에서 주로 쓰는 방식
RCSI OFF커밋된 값 10다음 문장 시작 전 커밋됐다면 8공유 잠금
RCSI ON문장 시작 시점의 커밋된 값 10새 문장 시작 시점의 커밋된 값 8행 버전

표에서 중요한 것은 두 번째 SELECT가 첫 번째와 다른 결과를 낼 수 있다는 점입니다. RCSI ON이 보장하는 단위는 각 문장입니다. 반대로 SNAPSHOT 격리수준은 활성화 전제와 함께 트랜잭션 단위 읽기 스냅샷을 제공합니다. 두 이름을 같은 것으로 취급하면 '문장 중간에 커밋된 변경을 다음 SELECT가 볼 수 있나'라는 문제에서 답이 바뀝니다.

읽기 잠금 완화가 쓰기 잠금 제거를 뜻하지는 않습니다

RCSI ON에서는 행 버전을 읽으므로 일반 READ COMMITTED SELECT가 읽은 데이터 행에 공유 잠금을 걸어 다른 트랜잭션의 수정을 막지 않습니다. 하지만 UPDATE나 DELETE 등 쓰기 작업은 수정할 데이터에 배타 잠금을 얻고 트랜잭션을 마칠 때까지 유지합니다. 그래서 'RCSI를 켜면 데이터베이스에서 잠금이 사라진다'고 말할 수 없습니다. 읽기와 쓰기의 동시성 동작을 분리해 설명해야 합니다.

또 행 버전 읽기는 모든 종류의 대기를 없애지 않습니다. SQL Server는 질의의 컴파일과 실행 중 스키마 안정성 잠금을 얻고, 동시 DDL이 스키마 수정 잠금을 보유하면 SELECT도 대기할 수 있습니다. 읽기에서 공유 행 잠금이 줄어든다는 설명을 '이제 아무 상황에서도 막히지 않는다'로 넓히지 마세요. 데이터 잠금 완화와 스키마·쓰기 잠금은 다른 경로입니다.

문제에서 설정·격리수준·시점을 함께 표시합니다

  1. 대상 제품이 SQL Server인지 Azure SQL Database 등 다른 제품인지 확인합니다.
  2. 데이터베이스의 READ_COMMITTED_SNAPSHOT이 ON인지 OFF인지 확인합니다.
  3. 세션이 READ COMMITTED인지 확인하고, 각 SELECT의 시작 시점과 다른 세션의 커밋을 시간순으로 둡니다.
  4. ON이면 문장 시작 때의 커밋 버전을, OFF이면 읽기 잠금 동작을 적용합니다.
  5. UPDATE 등 쓰기 잠금과 SNAPSHOT의 트랜잭션 스냅샷을 별도로 구분합니다.

흔한 오답은 RCSI를 SNAPSHOT의 줄임말처럼 보고 트랜잭션의 모든 SELECT가 같은 버전을 읽는다고 쓰는 것입니다. RCSI는 기본 READ COMMITTED 읽기를 문장 단위 행 버전으로 처리합니다. 또 '잠금 없는 읽기'라는 표현을 근거로 dirty read까지 허용한다고 생각해서도 안 됩니다. RCSI는 커밋된 행 버전을 제공하며, READ UNCOMMITTED나 NOLOCK과 같지 않습니다.

정답을 가리고 다음 SELECT의 값을 말해 보세요

SQL Server에서 RCSI ON인 데이터베이스에 A의 READ COMMITTED 트랜잭션이 있습니다. 첫 SELECT가 재고 10을 반환한 뒤 B가 재고 8을 수정하고 커밋합니다. A가 두 번째 SELECT를 실행하면 무엇을 볼까요? 두 번째 문장이 시작될 때 이미 8이 커밋됐으므로 A는 8을 볼 수 있습니다. 두 번의 SELECT가 같은 트랜잭션에 속해도 RCSI는 문장 단위 시점을 사용합니다. 이 결과만으로 트랜잭션 단위 SNAPSHOT이라고 답하면 안 됩니다.

정답을 가리고 확인해 보세요

이 글을 위해 만든 학습 예시이며 공식 기출문제나 판매 카드의 인용이 아닙니다.

RCSI ON에서 A의 첫 SELECT가 10을 반환한 뒤 B가 8을 커밋했습니다. READ COMMITTED인 A가 다음 SELECT를 하면 어떤 값을 볼 수 있나요?

정답과 해설 확인새 SELECT가 시작될 때 8이 커밋되어 있다면 A는 8을 읽습니다.

RCSI는 READ COMMITTED를 문장 단위 행 버전 읽기로 바꿉니다. 각 SELECT는 시작 시점의 커밋된 값을 읽으므로 다음 문장은 앞 문장과 다른 버전을 볼 수 있습니다.