다중 버전 동시성 제어(MVCC) 모델을 통한 데이터베이스 더티 리드(Dirty Read)의 원천적 물리 방어 결합
기술 면접관으로 참여하면서 가장 많이 질문하지만 가장 답변을 못 듣는 주제입니다. 개발자라면 꼭 알아야 할 내용을 제 주관적인 시각으로 풀어봤습니다.
전 세계 수백만 명의 접속이 동시다발적으로 쓰기와 읽기 충돌을 야기하는 관계형 데이터베이스 시스템(RDBMS) 환경에서, 고성능과 완벽한 데이터 무결성이라는 모순된 두 마리 토끼를 어떻게 모두 거머쥘 것인가는 튜링 어워드 수준의 아카데믹 패러독스였습니다. 수천 명의 유저 계정이 테이블에 접근할 때 상충되는 트랜잭션 하나를 보호하려고 거대한 테이블 전체나 수만 개의 로우(Row) 블록 구역 영역에 걸쳐 배타적 락(Exclusive Lock) 기둥 구조를 걸어둔다면 트랜잭션 동시성은 바닥을 치고 맙니다. 과거 은행 시스템들에서는 데이터를 변경하는 중인 레코드를 다른 일반 세션이 읽으려 시도할 경우 하염없이 큐에서 차단 대기 상태(Blocking)에 빠지는 악성 데드락 지연을 발생시켜 왔습니다. 특히 아직 영구 커밋(Commit) 되지 않은 롤백 파편 데이터를 가져와버리는 더티 리드(Dirty Read) 현상이나 읽기 시점 사이에 데이터가 몰래 증발해버리는 팬텀 리드(Phantom Read) 버그를 피해가기 위해선 거대한 락 오버헤드를 견뎌야만 했습니다.
이 끔찍한 병목 한계를 극복하고 현대의 강력한 시스템 스루풋을 견인해 낸 최고의 데이터 모델 설계가 바로 다중 버전 동시성 제어(MVCC, Multi-Version Concurrency Control) 아키텍처입니다. Oracle, PostgreSQL 관리자와 더불어 MySQL의 영혼인 InnoDB 엔진의 심장을 이루는 MVCC 모델 철학은 "읽기 세션은 결코 쓰기 세션을 블로킹하지 않으며, 쓰기 세션 또한 절대 읽기 세션을 가로막지 않는다"는 기조 하에 동작합니다. 누군가 계좌의 데이터 금액 값을 수정하는 UPDATE를 수행할 경우, 데이터베이스 백그라운드 엔진은 현재 존재하는 원본 수정 데이터를 통째로 날리지 않고 거대한 롤백 세그먼트 백스토어 저장소인 언두 로그(Undo Log) 공간에 과거 시점의 스냅샷 데이터를 몰래 옮겨 숨겨놓습니다.
동일한 타이밍에 다른 서버 노드의 조회 트랜잭션이 해당 로우의 값을 SELECT하려고 진입하면, 시스템 엔진의 트랜잭션 버저닝 매니저가 이 쿼리 엔진의 고유 논리적 시퀀스 번호 시점보다 늦게 변경된 더티 마스크 데이터들의 반환을 철저히 거절시킵니다. 그리고는 언두 로그로 연결된 체인 포인터 라인을 타고 들어가 과거 시점에서 보존되어 있던 타임머신 기록의 구버전(Old Version) 레코드 스냅샷 뷰 형상을 마치 현재 상태인 것처럼 가상 조작하여 즉각 꺼내어 돌려줍니다. 결국 각 연결 세션마다 서로의 렌즈 버전을 거쳐 각자 다른 차원의 타이밍 타임 라인 데이터를 병행 렌더링하고 있으므로 락을 걸지 않고도 그 어떠한 병목 충돌도 발생하지 않습니다. 데이터베이스는 수만 개의 히스토리 버전 기록들을 주기적으로 검사하여 전 세계 사용자 중 어느 누구의 격리 버전 모델 구간에도 더 이상 해당 과거 기록 영역이 참조되지 않음이 수학적으로 확인될 때에만 퍼지(Purge) 스레드를 개입시켜 아주 고요하게 낡은 데이터 쓰레기들을 영구 파기할 따름입니다.