멀티 버전 동시성 제어(MVCC, Multi-Version Concurrency Control) 개념은 많은 트랜잭션의 병행 처리를 보장하기 위해 엄격한 잠금의 대안으로 나타났습니다. 이는 데이터에 동시 접근할 때 충돌과 잠금을 줄이기 위해서 매우 중요했습니다. 특히 OLTP 시스템에서 더욱 그러합니다.
전통적인 잠금 방식(예: 행 잠금(row-level locking))은 높은 경쟁 상황에서 애플리케이션이 느려지게 만들 수 있습니다. MVCC의 목표는 트랜잭션이 기록 작업이 동시에 수행되더라도 일관된 데이터 스냅샷을 읽도록 하는 것으로, 이를 통해 격리성과 동시 접근이 보장됩니다.
MVCC는 인기 있는 DBMS(예: PostgreSQL, Oracle, MySQL/InnoDB)에서 행의 버전 히스토리를 저장함으로써 구현됩니다. 읽기 시, 각 트랜잭션은 시작 시점 이전에 커밋된 행만 보고, 삽입/업데이트는 즉시 삭제하지 않고 새로운 버전을 생성합니다.
쿼리 예제(PostgreSQL):
BEGIN TRANSACTION; SELECT * FROM orders WHERE status = 'processing'; UPDATE orders SET status = 'completed' WHERE id = 42; COMMIT;
트랜잭션이 완료될 때까지 다른 사용자는 행의 이전 버전을 보게 되며, 커밋 후에만 변화가 새로운 트랜잭션에 보여집니다.
핵심 특징:
MVCC가 모든 형태의 잠금과 충돌을 완전히 제거할 수 있나요?
아니요, MVCC에서도 동일한 행을 동시에 업데이트할 때는 충돌이 발생할 수 있습니다. 예를 들어, 동시에 UPDATE가 발생하면 커밋 충돌(write-write conflict)이 발생하고, DBMS는 오류를 발생시키거나 트랜잭션 중 하나를 롤백합니다.
MVCC에서 이전 버전의 행은 언제 삭제되며, 이것이 메모리 누수로 이어질 수 있나요?
대부분의 DBMS에서는 이전 버전의 행이 특별한 프로세스(VACUUM in PostgreSQL)에 의해 삭제됩니다. 이러한 프로세스를 실행하지 않으면 데이터베이스가 "팽창"하고 성능이 저하됩니다.
MVCC 환경에서 "select for update"는 제대로 작동하며, 왜 잠금이 필요한가요?
네, SELECT FOR UPDATE 쿼리는 병행 변경 시 충돌을 방지하기 위해 행을 잠급니다. 그렇지 않으면 "lost update"가 발생할 수 있습니다.
예:
BEGIN; SELECT * FROM products WHERE id = 123 FOR UPDATE; UPDATE products SET quantity = quantity - 1 WHERE id = 123; COMMIT;
큰 인터넷 쇼핑몰에서 VACUUM 설정 없이 빈번한 주문 업데이트 스키마가 구현되었습니다. 한 달 후 데이터베이스는 10배 증가하고 쿼리는 매우 느려졌습니다.
장점:
단점:
정기적인 autovacuum이 구현되고, write-conflict가 모니터링되어 중요한 쿼리에 대해서는 REPEATABLE READ 수준의 격리가 이루어졌습니다.
장점:
단점: