Undo 로그와 MVCC
date
‣
slug
db-undo-log
status
Published
tags
Database
summary
예시를 통해 Undo 로그와 MVCC에 대해 간단히 알아보자
type
Post
데이터베이스에서 동시에 여러 트랜잭션이 실행되는 상황은 흔하다. 이런 상황에서도 데이터의 일관성과 무결성을 보장하기 위해 다양한 메커니즘이 작동한다. 그중에서도 트랜잭션의 동시성과 관련된 대표적인 개념이 바로 Undo 로그와 MVCC(Multi-Version Concurrency Control)다.
간단한 예제를 통해 두 개의 트랜잭션이 서로 같은 행을 수정하거나, 전혀 다른 행을 수정했을 때 Undo 로그가 어떻게 동작하고, 왜 연관 없어 보이는 행까지 영향을 받는지에 대해 알아보자.
같은 행을 수정할 때 벌어지는 일
트랜잭션 A가 먼저 시작해서 어떤 행을 수정하고 아직 커밋하지 않은 상태에서, 트랜잭션 B가 동일한 행을 수정하려 한다고 해보자. 이 경우 대부분의 데이터베이스에서는 B의 접근을 막는다.
그 이유는 A가 해당 행을 수정하면서 Exclusive Lock(배타적 잠금)을 걸었기 때문이다. B는 A가 작업을 마치고 커밋하거나 롤백할 때까지 기다리게 되거나, 경우에 따라 교착 상태가 발생해 에러가 날 수도 있다.
서로 다른 행을 수정하면?
이번에는 A와 B가 전혀 다른 행을 수정한 경우를 생각해보자. A는 먼저 시작했지만 아직 커밋하지 않았고, B는 나중에 시작해서 수정을 마친 후 커밋까지 완료했다. 겉보기에 충돌은 전혀 없고, 실제로 두 트랜잭션은 독립적으로 잘 작동한다.
하지만 이 경우에도 B의 Undo 로그가 당장 삭제되지는 않는다. 이유는 바로 A의 트랜잭션이 끝나지 않았기 때문이다.
Undo 로그는 왜 남아 있어야 할까?
커밋이 완료된 트랜잭션이라면 Undo 로그도 더 이상 필요 없을 것 같지만, 실제로는 그렇지 않다. 그 이유는 데이터베이스가 사용하는 MVCC 방식과 관련이 깊다.
MVCC는 여러 트랜잭션이 동시에 실행되더라도 각자 시작 시점의 데이터 상태를 기준으로 일관된 결과를 보도록 보장하는 방식이다. 예를 들어 A가 먼저 시작했고, 그 이후 B가 어떤 데이터를 수정하고 커밋했다고 해도, A는 여전히 자기 기준 시점의 데이터를 읽어야 한다.
따라서 B가 수정한 데이터가 A의 스냅샷 시점과 달라졌다면, A가 그 데이터를 읽으려고 할 때 Undo 로그를 통해 이전 버전으로 복원해 보여줘야 한다. 이 때문에 B의 Undo 로그는 A가 종료되기 전까지는 삭제할 수 없다.
A는 해당 데이터를 읽지 않을 수도 있는데?
자연스럽게 드는 의문일 수 있다. A가 B가 수정한 행을 전혀 건드리지도 않고, 읽지도 않을 거라면, Undo 로그를 굳이 남겨둘 필요가 있을까?
결론부터 말하면, 그럴 가능성이 있으면 무조건 보존해야 한다. 트랜잭션 A는 아직 커밋되지 않았기 때문에 언제든 어떤 쿼리를 날릴 수 있다. 예를 들어 A가 이후에
SELECT * FROM users WHERE age < 35 같은 쿼리를 실행할 수도 있다. 이 쿼리는 B가 수정한 데이터를 포함할 수도 있고 아닐 수도 있지만, 데이터베이스는 그걸 미리 알 수 없다.결국 안전하게 과거 버전을 보존해두고, 필요할 경우 제공할 수 있어야 MVCC가 제대로 작동한다. 이게 Undo 로그가 B와 직접 관련 없는 A 때문에 오랫동안 살아 있게 되는 이유다.
A가 롤백하면 B도 되돌려지나?
그렇지 않다. 트랜잭션 A가 롤백하더라도, 이미 커밋이 완료된 B는 전혀 영향을 받지 않는다. Undo 로그가 살아 있었던 건 단지 A가 과거 데이터를 읽을 가능성이 있었기 때문이지, A가 B를 되돌릴 수 있었던 건 아니다.
데이터베이스는 트랜잭션 단위로 독립성을 보장한다. 하나의 트랜잭션이 실패하거나 취소되더라도, 다른 트랜잭션의 결과는 그대로 유지된다.
정리
- MVCC는 트랜잭션마다 고정된 시점의 세상을 보여주는 방식이다.
- 이 방식을 위해선 이전 상태의 데이터를 복원할 수 있는 Undo 로그가 필요하다.
- 한 트랜잭션이 커밋되었더라도, 다른 오래된 트랜잭션이 끝나기 전까지 Undo 로그는 삭제되지 않는다.
- 내가 수정하지 않은 행의 Undo 로그조차 살아 있을 수 있는 건, 누군가 그 데이터를 나중에 읽을 수 있기 때문이다.
- 이런 구조 덕분에 여러 트랜잭션이 동시에 실행되면서도 데이터는 언제나 일관성 있게 유지된다.