파일 잠금과 동시성

파일 잠금과 동시성

SQLite는 상당히 얇은 라이브러리인데, "무엇이 정말 내부에서 벌어지고 있나"를 알고 싶다면 잠금(locking)과 동시성 제어를 이해해야 해요. 이 문서는 SQLite 버전 3에서 데이터베이스 파일 잠금이 어떻게 작동하는지를 설명하는 권위 있는 참고 자료예요. 여기서는 특히 오래된 롤백 모드 트랜잭션 메커니즘의 잠금만 다루며, WAL 모드의 잠금은 별도 문서에서 설명돼요.

출처: https://www.sqlite.org/lockingv3.html

페이저(pager) 모듈과 잠금

잠금과 동시성 제어는 페이저(pager) 모듈이 담당해요. 페이저는 SQLite를 "ACID"(Atomic, Consistent, Isolated, Durable)하게 만드는 역할을 해요. 모든 변경이 한꺼번에 일어나거나 전혀 일어나지 않게 하고, 두 개 이상의 프로세스가 동시에 호환되지 않는 방식으로 데이터베이스에 접근하지 못하게 하며, 커밋된 변경이 명시적으로 삭제될 때까지 지속되게 해요.

페이저 입장에서 데이터베이스는 균일한 크기의 블록으로 이뤄진 단일 파일이에요. 각 블록을 "페이지(page)"라고 부르고, 보통 1024바이트 크기예요. 페이지는 1부터 번호가 매겨져요.

다섯 가지 잠금 상태

단일 프로세스 관점에서 데이터베이스 파일은 다섯 가지 잠금 상태 중 하나예요.

상태 설명
UNLOCKED 잠금이 전혀 없음. 읽지도 쓰지도 못함. 기본 상태.
SHARED 읽을 수 있지만 쓸 수는 없음. 여러 프로세스가 동시에 SHARED 잠금을 가질 수 있어 여러 동시 읽기가 가능. 다만 SHARED 잠금이 하나라도 있으면 다른 프로세스는 파일에 쓸 수 없음.
RESERVED 프로세스가 나중에 파일에 쓸 계획이지만 지금은 읽기만 하는 상태. RESERVED는 한 번에 하나만 가능하지만, 여러 SHARED와 공존할 수 있음.
PENDING 프로세스가 가능한 한 빨리 쓰기를 원하고, SHARED 잠금이 모두 사라지길 기다리는 상태. PENDING이 있으면 새 SHARED 잠금은 허용되지 않지만 기존 SHARED는 계속 유지됨.
EXCLUSIVE 쓰기에 필요한 잠금. 파일에 하나만 허용되고, 다른 어떤 잠금과도 공존할 수 없음. 동시성을 최대화하려고 EXCLUSIVE 시간을 최소화함.

롤백 저널

쓰기 트랜잭션 시작 시(비 WAL 모드) 프로세스는 먼저 원래 데이터베이스 내용을 롤백 저널(rollback journal) 에 기록해요. 롤백 저널은 데이터베이스 파일과 같은 디렉토리에 있고, 데이터베이스 파일명에 -journal 접미사가 붙은 이름의 일반 파일이에요. 데이터베이스 파일 크기도 기록해서, 롤백 시 파일이 커졌다면 원래 크기로 잘라낼 수 있어요.

뜨거운 저널(hot journal) 은 무결성을 복원하기 위해 롤백이 필요한 저널을 말해요. 프로세스가 데이터베이스 갱신 중에 크래시나 정전이 나서 갱신이 완료되지 못했을 때 생겨요. 정상 동작 중에는 뜨거운 저널을 보지 못하는 것이 정상이에요.

파일을 읽기 전에 SQLite는 항상 뜨거운 저널이 있는지 검사하고, 있으면 파일을 읽기 전에 롤백해요. 그래야 데이터베이스 파일이 읽기 전에 일관된 상태가 되거든요.

쓰기 과정의 잠금 순서

쓰기 프로세스는 다음 순서로 잠금을 얻어요.

  1. SHARED 잠금 획득 (뜨거운 저널이 있으면 먼저 롤백).
  2. RESERVED 잠금 획득 — 미래에 쓸 계획을 알리는 신호. RESERVED는 한 번에 하나만 가능하고, 다른 프로세스는 계속 읽을 수 있어요. RESERVED를 얻지 못하면 다른 프로세스가 이미 RESERVED를 갖고 있다는 뜻이고, 쓰기는 SQLITE_BUSY로 실패해요.
  3. 커밋 시 EXCLUSIVE 잠금이 필요해지면, PENDING을 거쳐 EXCLUSIVE로 승격.

기본적으로 SQLite 3은 자동 커밋(autocommit) 모드로 동작해요. BEGIN TRANSACTION(TRANSACTION 키워드는 선택)으로 자동 커밋을 끄고, COMMIT은 실제로 디스크에 커밋하지 않고 자동 커밋을 다시 켜서 실제 커밋을 유도해요. ROLLBACK도 자동 커밋을 켜고 롤백 플래그를 설정하는 방식이에요. 이 설계 덕분에 파일에 대한 읽기 차단이 최대한 늦게 일어나요.

주의할 점

  • 데이터베이스 파일에 하드·소프트 링크를 만들지 않는 게 좋아요. 저널을 찾지 못하는 문제가 생길 수 있어요.
  • 데이터베이스와 저널을 각자 디렉토리에 두거나 저널링 파일시스템을 쓰면 정전 시 저널 분실 위험을 줄일 수 있어요.
  • 여러 데이터베이스를 ATTACH로 함께 쓰는 컴밋은 슈퍼저널(super-journal)이 관여하므로, 서로 다른 디스크 볼륨에 두면 원자성이 깨질 수 있어요. 같은 볼륨에 두는 게 안전해요.

더 알아보기