[데이터베이스] Transaction #1 회복

5 분 소요

Transaction


Transaction은 한글로 ‘거래’ 라는 뜻으로, 데이터베이스 에서는 고급이론으로 두개의 타이틀인 ‘병행제어’ 와 ‘회복’ 으로써 정립된다.

본래 Transaction은 본어 그대로 은행에서의 ‘거래’를 가장 많은 예시로 꼽는다.

A라는 사람이 B라는 사람에게 만원을 송금했다고 가정해보자. A의 계좌에서는 만원이 차감되어야하고, B의 계좌에서는 만원이 증감되어야 한다.

우리는 이러한 두개의 논리적 연산이 한번에 이루어져야 한다. 둘다 발생하던지 아니면 둘다 발생하지 않아야한다.

만약, A계좌에서는 만원이 차감됬는데 B계좌에는 만원이 증감되지 않으면 누가 이은행의 시스템을 신뢰하겠는가??

또한, 동일한 데이터에 두개이상의 작업이 동시에 접근했을때 데이터의 무결성에 오류가 발생할 여지가 있다.

동일계좌에서 만원을 출금하는 연산을 두명이상이 동시에 수행했을때, 출금이 모두 성공한다면 은행은 거지가 될것이다.

이러한 하나의 작업단위에 모아진 물리적연산(데이터베이스의 연산)들을 모아놓은 하나의 논리적 연산을 데이터베이스 에서는 ‘Transaction’ 이라고 한다.

특성


Transaction은 4가지의 특성을 가진다.

  • 원자성(Automicity) : 더이상 쪼개어질수 없는 성질이다. 즉 하나의 작업은 쪼개어질 수 없다는 의미이다. All or Nothing (모두 수행되던지, 아니면 아무것도 수행되지 말던지)
  • 일관성(Consistency) : 작업이 수행된후 항상 데이터는 일관성을 가져야 한다. 트랜잭션이 수행되기 전과 수행후가 일관성이 있어야 한다. A계좌에 만원이 있는데 5천원을 B계좌로 입금한다 했을때 연산전의 총금액이 만원이고 연산후의 총금액도 만원이 되어야만 한다.
  • 격리성(Isolation) : 수행중인 트랜잭션에서 다른 트랜잭션이 간섭할수 없다. 내가 계좌에서 만원을 빼고있는데, 부모님이 똑같이 만원을 빼서 2만원을 출금하면 은행은 호구가된다.
  • 지속성(Durability) : 작업이 수행된 후에도 데이터는 영구적으로 비휘발성 저장장치에서 지속적으로 유지되어야 한다. 만원을 내계좌에 송금했는데 일주일뒤에는 0원이되면? 멱살을..

원자성과 지속성은 ‘회복’에 부합하는 특성이고, 일관성과 격리성은 ‘병행제어’에 부합되는 특성이다.

LOG


흔히 데이터베이스에서 commit 연산을 수행했다고 해서 그내용이 반드시 그순간에 디스크에 반영되는 것이라는 착각을 할 수있다.

DBMS는 응용프로그램이고 메인메모리에서 수행이된다. 메인메모리에서 보조기억장치에 write 연산을 수행하는 것은 OS가 담당하며, 이과정은 언제 수행될지 사용자, 개발자는 알수 없다. OS가 담당하니까…

즉, Commit 연산이라는 행위는 데이터베이스의 갱신연산이 디스크에 쓰여질 것 임을 보장하는 행위이다. 마치 우리가 월세계약을 할때, 계약서에 도장을 찍는건 임차인과 임대인이 같이하지만 구청에가서 해당 계약서를 제시하고 전입신고를 할때는 임차인이 혼자가서 하는것 처럼 임대인은 임차인이 전입신고를 언제할지는 모르는 상황 처럼 말이다. 하지만 계약서는 이미 작성됬으니 임대인은 신경쓸 바가 아니다.

하지만, 데이터베이스는 방대하고 대용량 이면서 데이터가 산재되어 있기 때문에 (DBMS 에 따라 다르겠지만..) 원하는 위치에 그내용들이 실제로 디스크에 쓰이기 까지는 시간이 걸리게되고 (언제인지는 os만 알고, 우리는 알수없다) 그사이에 장애가 발생한다던지… 컴퓨터 선을 사랑스러운 애완동물들이 날라차기를 해서 컴퓨터가 꺼져버린다던지 하면 휘발성메모리인 메인메모리의 내용이 날아가면서 디스크에 쓰이지 않을수도 있다.

그런 사태를 방지하기 위해 DBMS는 ‘LOG’를 이용한다. 로그는 흔히 우리가 게임에서 접한다. 게임내에서 사용자의 어떠한 행위라도 일어나게되면 그내용이 전부다 하나하나 .log 라는 곳에 적힌다는 사실은 게임을 하는사람은 모두 안다.

그런 것처럼 DBMS는 로그라는 파일에 DBMS가 commit연산을 수행하여 메인메모리에 있는 연산된 내용을 디스크에 쓰기전에 아주 간략하게 적는다.

그리고 장애가 발생할때 ‘LOG’ 를 참고하여 디스크에 데이터를 write하게 된다.(Redo)

이를 ‘로그우선규약’ 이라고 한다.

로그 우선 규약에 따라 DBMS에서 수행된 연산이 데이터베이스에 반영되는 일련의 과정을 표현하자면,

Main Memory(Buffer) -> Log -> DataBase 의 순서로 데이터가 write 된다. 로그와 데이터베이스는 Disk 공간에 있다.

회복


앞서 설명된 원자성과 지속성이 회복에 부합하는 특성이라고 했다. 이들이 어떻게 회복에 적용되는지 살펴보자.

원자성은 뗄레야 뗄수없는 더이상 쪼갤수없는 특징을 원자성이라고 한다. 즉 All or Nothing으로써 하나의 작업은 모두 수행되던지, 아니면 아에 수행되지말던지 둘중에 하나가 되어야한다.

로그에는 일련의 작업인 Transaction 내에서의 연산과정이 적히게 되는데 일종의 규칙이 적용된다.

  • <트랜잭션A, start> 이는 트랜잭션 A가 시작된다는 의미이다. 즉 하나의 작업 A라는 것이 시작한다는 의미
  • <트랜잭션A, x, 10,20> 이는 트랜잭션 A에서 x라는 값이 이전값은 10이고 이후값은 10이라는 의미이다. 즉 x가 10에서 20으로 10만큼 증가했다는 의미
  • <트랜잭션A, y, 5, 15> 이는 트랜잭션 A에서 y라는 값이 이전값은 5이고 이후값은 15라는 의미이다.
  • <트랜잭션A, commit> 이는 트랜잭션 A가 commit 연산되어 종료되었다는 의미이다.

위와같이 하나의 작업은 start로 시작하고 commit으로 종료된다. 그리고 그 작업내에서의 연산과정들은 어떤 데이터와 그데이터의 이전값과 이후값으로 적힌다.

만약 여기서 x값이 적히고 나서 장애가 발생했다고 가정해보자.

<트랜잭션A, start> 
<트랜잭션A, x, 10,20>    

 장애  발생
 
<트랜잭션A, y, 5, 15> 
<트랜잭션A, commit> 

장애가 발생한 곳은 y값이 적히고 commit 연산이 수행되기 전이다. 트랜잭션 A는 원자성을 가지고 해당 4개의 연산들은 모두 쪼갤수없다.

따라서, commit이 수행되기 전에 장애가 발생하면 이모든 연산은 수행되지 않는 Nothing이 되어야만 한다. 마치 계약서가 4장인데 2장은 싸인을했고, 2장을 읽다보니 수상한점이 발생해서 엎으려고 하는 경우에서 두장은 싸인을했으니 두장은 계약이 실행되는가? 아니다. 4장이 모두 Nothing이 되어야만 한다.(물론 여기서는 Exception과 같은 장애가 발생했다는 전제)

따라서 위내용은 Undo = Rollback 되어야만 한다.

Rollback 이란, 트랜잭션의 상태에서 ‘철회’ 상태로써 해당 트랜잭션의 수행이 실패하여 트랜잭션이 일어나기전으로 되돌려놓는 행위를 말한다.

만약 commit이 일어나고 나서 장애가 발생하면 어떨까?

<트랜잭션A, start> 
<트랜잭션A, x, 10,20>    
<트랜잭션A, y, 5, 15> 
<트랜잭션A, commit> 

 장애 발생
 

해당 트랜잭션의 commit 연산이 수행되어 디스크에 쓰여질것임은 보장되어 진다. 즉, 이때는 redo 된다.

메인메모리는 휘발성 메모리이고 위에서 얘기했다시피 실제 디스크에 쓰여질때 까지는 시간이 걸리기 때문에 commit 이후에 장애가 발생하면 DBMS는 그내용이 실제 디스크에 쓰였는지, 아닌지를 알수 없다. 따라서 이때는 반드시 Log에 있는 내용을 redo 하여 디스크에 write 연산을 수행한다.

즉시갱신과 지연갱신


로그에 있는내용을 장애가 언제 발생하느냐에 따라 Redo(commit이후이면), Undo(commit 이전이면) 를 수행한다고 했다.

DBMS에 따라 이러한 갱신작업을 즉시 할수도 있고 지연 할수도 있다.

즉시갱신은 갱신연산이 하나하나 수행될 때 마다 DB에 반영하는 방법이다.

<트랜잭션A, start> 
<트랜잭션A, x, 10,20>    

 장애  발생
 
<트랜잭션A, y, 5, 15> 
<트랜잭션A, commit> 

위에서 설명한바와 같이 즉시갱신에서는 해당위치에서 장애가 발생하면 undo가 수행되어 Rollback 되어진다.

이때 로그에 있는 정보에 따라 x값의 갱신연산이 적혀있으므로 x값을 10으로 바꾸는 rollback 연산이 이루어 진다.

<트랜잭션A, start> 
<트랜잭션A, x, 10,20>    
<트랜잭션A, y, 5, 15> 
<트랜잭션A, commit> 

 장애 발생
 

또한 commit 이후라면 위에서 설명한 바와 같이 redo가 수행되어 로그에 있는 내용을 디스크에 쓴다.

하지만 지연갱신은 조금 다르다.

지연갱신은 commit 연산이 수행 될 때마다 DB에 반영하는 방법이다.

즉, 메인메모리(버퍼)와 로그에 데이터베이스의 연산들이 일어난것을 기록해두고 이내용들이 commit이 수행되면 DB에 기록된다.

<트랜잭션A, start> 
<트랜잭션A, x, 20>    

 장애  발생
 
<트랜잭션A, y, 15> 
<트랜잭션A, commit> 

지연갱신에서 위상황은 commit이 수행되기 전이므로 DB에 기록되어져 있지 않은 상태다. 즉 장애가 발생하면 그냥 없던일로 취급하고 무시 되며, 해당 버퍼와 로그의 내용만 버리면 된다.

<트랜잭션A, start> 
<트랜잭션A, x, 20>    
<트랜잭션A, y, 15> 
<트랜잭션A, commit> 

 장애 발생
 

또한 위상황에서는 commit 수행 이후 이므로 로그에 기록되어져 있고, commit이후 이므로 당연히 로그의 내용을 토대로 redo 한다.

즉, 지연갱신의 차이점은 undo를 하지 않는다는 것이다.

위 트랜잭션에서 볼수 있듯이 이전값은 적혀있지않고 이후값만 적혀있다. undo가 필요없기 때문에

그러면 이런 의문점이 들수 있다.

로그의 내용은 갱신연산 하나하나를 기록하기 때문에 데이터베이스보다 더 방대한 양으로 커질것이고, 거기서 또 commit이후 인지 이전인지 찾는데는 시간이 오래 소요될수 있지않은가?

물론이다. 로그의 내용은 점차 축적되면서 방대해지기 때문에 로그를 관리하는것도 필요하다. 따라서 주기적으로 로그를 백업하고 삭제해주어야 한다.

또한 이를위해 DBMS는 검사점(Checkpoint) 라는 것을 이용하는 검사시점회복기법 을 제공한다.

트랜잭션들을 수행하고 나서 디스크에 쓰여졌다면 검사점(CheckPoint)를 레코드에 삽입하여, 회복이 필요할때 모든 로그를 검색하지 않고 가장 최근의 검사점 이후의 내용들만 가지고 회복을 수행한다.

<트랜잭션A, start> 
<트랜잭션A, x, 10, 20>    
<트랜잭션A, y, 10, 15> 
<트랜잭션A, commit> 

<Checkpoint>

<트랜잭션B, start> 
<트랜잭션B, x, 20, 30>    
<트랜잭션B, y, 15, 25> 
<트랜잭션B, commit>

<트랜잭션C, start> 
<트랜잭션C, z, 10, 30>  

장애 발생

<트랜잭션C, y, 25, 45> 
<트랜잭션C, commit>

위의 예제에서는 checkpoint 이전의 내용들은 디스크에 쓰여졋기 때문에 무시한다.

또한, 이전값과 이후값이 모두 존재하기 때문에 즉시갱신에 해당하며 트랜잭션B는 commit 이후이기 때문에 redo를 수행한다.

트랜잭션C에 대해서는 Undo (Rollback)을 수행하여 z를 10으로 바꾼다.

위내용을 통해 트랜잭션의 첫번째 타이틀인 회복기법을 이용하는데 사용되는것은 LOG 이고 이를통해 Redo 혹은 Undo 를 통해 트랜잭션의 원자성(Automicity)과 지속성(Durability)를 보장하는것을 살펴보았다.

태그:

카테고리:

업데이트:

댓글남기기