Transaction이란, 데이터베이스 작업을 수행하는 단위 프로세스로, 데이터 공유와 다수 사용자 처리를 위해 사용된다. Transaction이 적용되면 여러 명의 사용자가 동시에 동일한 리소스에 접근하더라도, 데이터베이스의 일관성이 보장된다. 이로써 모든 사용자가 동일한 데이터 베이스를 공유할 수 있게 되고, 동시성이 제어될 수 있다.
Transaction은 Jim Gray가 제안한 ACID를 따르며, 모든 데이터베이스는 ACID 원칙을 준수해야 한다.
① Atomicity(원자성)
② Consistency(일관성)
③ Isolation(격리성)
④ Durability(영속성)
원자성을 위한 연산을 이해하기 위해선 먼저, 데이터베이스 저장 연산이 내부적으로 어떻게 이루어지는지 알아야 한다. 저장장치는 아래의 3가지 유형으로 분류될 수 있다.
① Volatile Memory
② Non-Volatile Memory
③ Stable Storage
그 중에서도 RAM과 Disk 사이에서 일어나는 저장 연산의 프로세스를 알아보기로 하자. Disk의 latency(응답 속도)는 RAM에 비해 매우 느리기 때문에, RAM에서 연산이 수행되는 시간과 그것이 Disk에 영구적으로 반영되는 시간 사이에는 간격이 존재할 수 밖에 없다. 만약 연산은 정상적으로 수행되었지만, 디스크에는 반영되지 않았을 경우 Failure가 발생하게 된다.
조금 더 구체적인 예시를 생각해보자. 1000원이 있는 A의 계좌에서 2000원이 있는 B의 계좌로 100원을 이체하려고 할 때, 일어나는 프로세스는 아래와 같다.
① Disk를 읽어 A의 계좌 잔액을 알아낸다.
② A의 잔액에서 100원을 빼는 연산을 수행한다.
③ 위 연산의 수행 결과를 디스크에 반영(write)한다.
④ 다시 Disk를 읽어 B의 계좌 잔액을 알아낸다.
⑤ B의 잔액에서 100원을 더하는 연산을 수행한다.
⑥ 위 연산의 수행 결과를 디스크에 반영(write)한다.
RAM에서 B의 잔액을 계산하는 연산을 성공적으로 끝마쳤지만, 이를 Disk에 write하는 과정(⑥에 해당)에서 failure가 발생했다고 하자. 그 때의 RAM과 Disk의 상태는 아래와 같을 것이다.
Failure가 발생함으로 인해, 100원이 손실되었다. 이처럼 Failure로 인해 데이터베이스가 일관성을 상실한 상태를, Inconsistent State라고 부른다.
Transaction의 원자성을 보장하기 위한 연산으로 Commit과 Rollback이 있다.
① Commit
② Rollback
쉽게 말해, Commit한 시점에 갱신이 반영되며, Commit 하기 전에 Rollback 함으로써 갱신을 반영하지 않을 수 있다. 이를 그림으로 나타내면 아래와 같다.
Transaction Status는 Active(활동), Partial Commited(부분 완료), Failed(실패), Commited(완료), Aborted(철회)로 구성된다. 위 그림은 100% 성공이 아니면 모두 Rollback 해야 함을 의미하고 있다. Aborted 상태에서는 Transaction을 재실행하거나 (기존 Transaction은 폐기하고) 다른 Transaction을 대기할 수 있는데, 어떤 방법을 선택할지는 DBA의 design choice에 맡긴다.
Transaction Recovery란, 데이터베이스를 장애 발생 이전의 Consistent State로 복원시키는 것을 의미한다. Transaction Recovery에는 Recovery Manager라고 하는 DBMS의 Sub System이 관여하는데, 이와 관련한 코드가 전체 DBMS 코드의 10%를 차지할만큼 신뢰성 있는 회복은 매우 중요하다. Transaction의 회복을 위한 방법에는 Redo와 Undo가 있다.
① Redo
② Undo
Redo와 Undo 모두 Log를 활용한 회복 방식이다. Redo와 Undo의 동작 방식을 대강 살펴보면 아래와 같다.
① Online Log
② Archive Log
이와 비슷하게 Online Database는 변경 내용을 백업 및 기록하고, Archive Database는 데이터를 오랜 기간동안 보존한다.
DBMS의 저장 구조는 아래와 같다.
예를 들어 DBMS 코드에서 "1+1=2"라는 연산을 수행한다고 하자. 이 연산의 결과는 로그 버퍼와 DB 버퍼로 전달되는데, 반드시 로그 버퍼에 먼저 저장되어야 한다. 이것을 WAL(Write Ahead Logging)이라 한다. WAL은 Consistency와 Atomicity를 보장하기 위해 사용된다.
Database의 원활한 회복을 위해서는 Logging을 자주 해야하지만, 너무 많은 Log는 Database의 성능을 저하시킬 수 있다. 따라서, 저장장치의 효율성 및 신속한 회복을 목적으로 Log 압축을 사용한다.
Log 압축은 Log를 실제로 압축시킨다는 의미가 아니라, 불필요한 로그를 저장하지 않는다는 의미이다. 불필요한 로그에는 아래와 같은 것들이 포함될 수 있다.
① 실패한 Transaction에 대한 로그
② 성공한 Transaction의 갱신 전 데이터에 대한 로그
Redo와 Undo를 이용해 데이터베이스를 복구할 때, 어떤 Transaction에 대해 Redo와 Undo가 필요한지 결정해야 한다. 하지만, 이를 위해 Log를 Full Scan하는 것은 너무나도 많은 시간을 필요로 한다.
이 때, 사용할 수 있는 방법이 Recovery with Checkpoints(검사시점 회복)이다. Checkpoints는 데이터베이스 시스템에서 주기적으로 발생하는 검사시점으로, 이 시점에서 데이터베이스는 Consistent State로 간주되며, 현재까지의 트랜잭션들이 디스크에 반영된다.
Checkpoints를 활용하여 Redo와 Undo를 결정하는 방법은 아래와 같다.
아래의 그림에서 T2와 T4는 Redo의 대상, T3, T5는 Undo의 대상이 되며, T1은 별도의 Recovery 작업을 필요로 하지 않는다.