공부방/데이터베이스

말만 들어도 어려워 보이는 트랜잭션에 대해 자세히 살펴보자 2편

EVO. 2023. 5. 30. 17:25

https://babgeuleus.tistory.com/59

 

말만 들어도 어려워 보이는 트랜잭션에 대해 자세히 살펴보자 1편

개요 프로젝트를 진행하면서 가끔씩 service부분에서 @Transaction이 readOnly=true로 되어있어 현재 수행하는 작업 단위(save,update등)가 불가능하다는 오류가 발생했다는 에러코드가 뜹니다. 물론 제가

babgeuleus.tistory.com

이전 시간에는 트랜잭션의 특성과 상태에 대해 공부를 하였습니다.

 

이번 시간에는 장애와 회복에 대해 공부를 해보겠습니다.

 


장애와 회복

회복이란 장애가 발생하였을 때 데이터베이스를 장애가 발생하기 전의 일관된 상태로 복구시키는 것을 말합니다.

 

그렇다면 장애는 어떤 유형을 말하는 걸까요?

 

1. 트랜잭션 장애

트랜잭션의 논리적 오류, 잘못된 데이터 입력 , 시스템 자원의 과다 사용 요구 , 처리 대상 데이터의 부재 등을 말합니다.

 

2. 시스템 장애

하드웨어 이상으로 메인 메모리에 저장된 정보가 손실되거나 교착 상태가 발생한 경우 등을 말합니다.

 

3. 미디어 장애

디스크 헤드의 손실이나 고장등으로 디스크에 저장된 데이터베이스의 일부 혹은 전체가 손상된 상태를 말합니다.

 


 

이러한 장애를 회복하기 위해 2가지 방법을 통해 먼저 두가지 형식의 복사본을 저장합니다.

 

첫번째, 덤프(dump): 하루에 한번 또는 한달에 한번 미리 정해진 주기에 따라 디스크와 같은 비휘발성 저장 장치에 데이터베이스 복사본을 저장합니다.

두번째, 로그(log): 데이터베이스에서 변경 연산이 실행될때마다 데이터를 변경하기 이전 값과 변경한 이후의 값을 별도의 파일에 기록하는 방법입니다, 데이터손실이 발생하지 않는 저장 장치에 저장해둡니다.

 


로그란, 데이터베이스에 대한 변경 연산과 관련하여, 데이터를 변경하기 이전의 값과 변경한 이후의 값을 저장합니다.

이러한 로그들을 저장한 파일을 로그파일 이라고 하는데 로그파일은 레코드 단위로 기록됩니다. 즉 , 로그파일 한줄한줄이 레코드 단위라고 보시면 됩니다.

 

데이터베이스에 대한 변경 연산은 트랜잭션 단위로 수행되므로 로그 레코드도 트랜잭션이 실행될때마다 로그파일에 이전값과 변경 이후 값을 저장합니다.

 

코드로 조금 더 설명해보겠습니다.

어떤 응용프로그램이 계좌이체라는 트랜잭션을 수행한다하면 바로 아래 코드 처럼 실행을 할 것 입니다.

 

계좌이체 트랜잭션

read(X); //메인 메모리 버퍼 블록에 저장되있는 데이터 X를 프로그램의 변수로 읽어오는 연산 10000원
X=X-5000; //읽어온 데이터에 5000을 빼 5000을 변수에 저장
write(X); //데이터 5000 변수을 메인 메모리 버퍼 블록에 있는 데이터 X에 기록하는 연산
read(Y); //데이터 Y를 가져옴
Y=Y+5000; //변수에 5000을 추가
write(Y); //메인메모리에 5000을 데이터 Y에 기록

그리고 이 계좌이체의 트랜잭션을 로그파일에도 기록을 할 것 입니다.

=> 로그파일에 기록된 로그 레코드

1: <T1,start> //트랜잭션 T1가 수행을 시작했음을 기록
2: <T1, X, 10000, 5000> //트랜잭션 T1가 데이터 X를 이전값 10000에서 새로운 값 5000으로 변경하는 연산을 실행했음을 기록
3: <T1, Y, 0, 5000> //트랜잭션 T1가 데이터 Y를 이전값 0에서 새로운 값 5000으로 변경하는 연산을 기록
4: <T1, commit> // 트랜잭션 T1가 성공적으로 완료되었음을 기록

 

디스크에 저장되는 로그 파일이 언제 기록 되느냐는 로그 회복기법의 종류에 따라 다릅니다.

 

로그 회복 기법

- 즉시 갱신 회복 기법

트랜잭션 수행 중에 데이터를 변경한 연산의 결과를 데이터 베이스에 즉시 반영 합니다. 그리고 장애 발생에 대비하기 위해 데이터 변경에 대한 내용을 로그 파일에도 기록을 하는데 트랜잭션에서 데이터 변경 연산이 실행되었을 때 로그 파일에 로그 레코드를 먼저 기록한 후 데이터베이스에 변경 연산을 반영해야 합니다.

 

예를들어 로그파일 2번째 줄을 먼저 기록하고 나서 데이터베이스에 X=5000이라고 기록되고 , 3번째 줄을 먼저 기록하고 Y=5000이라고 데이터베이스에 기록합니다.

 

이렇게 기록을 하다가 장애가 발생하면 로그 파일에 기록된 내용을 참조하여 , 장애 발생 시점에 따라 redo나 undo 연산을 실행하여 데이터베이스를 복구합니다. 


undo연산을 수행해야하는 조건 : 로그 파일에 <Ti,start> 로그레코드는 존재하지만 <Ti,commit> 로그 레코드는 존재하지 않은 상태입니다.

 

이때는 트랜잭션이 수행되다 도중에 끊긴 상태입니다. 예를들어 로그파일에서 2번째 줄까지만 기록된 상태라면 디스크에 X계좌는 5000이 되었을수도 있지만 Y계좌는 무조건 0인상태입니다. 아직 3번째 줄이 기록되지 않은 상태이기 때문이죠. 이때는 데이터베이스가 undo연산을 실행하여 지금까지 실행된 모든 연산을 취소하여 데이터베이스를 원래의 상태로 복구합니다. 즉 로그파일에 기록된 순서의 반대로 undo연산을 진행합니다.

로그파일의 2번째 줄을 먼저 보면 이전값이 10000으로 적혀있으니 데이터베이스에 X에 10000을 기록하는 것이죠.

 

 

redo연산을 수행해야하는 조건: 이번에는 장애가 발생했을 때 로그파일을 보니 <Ti,start> 로그 레코드와 <Ti,commit> 로그레코드가 모두 존재하는 상태 입니다. 

 

이때는 트랜잭션이 모두 수행된 상태입니다. 하지만 디스크에 반영이 되었는지는 알수 없는 상태이죠

(RDBMS가 운영체제에게 버퍼에 기록된 값을 디스크에 기록해달라고 요청하고 운영체제가 버퍼에 있는 값을 디스크에 쓰라고 요청을 하지만 디스크는 언제 이 값을 쓸지 아무도 모릅니다.) 그러니 장애가 발생했으면 기록됐는지 알수가 없기 때문에 확실하게 하기 위해 가장 최근에 저장한 데이터베이스 복사본을 가져온 후 로그파일을 이용해 로그레코드에 기록된 순서대로 1번부터 실행합니다. 2번을 보고 X에는 5000을 적어야 되구나 하고 데이터베이스 복사본에 5000을 적고 3번을 본다음 데이터베이스복사본의 Y에 5000을 적는 모든 변경연산을 재실행하여 장애가 발생하기 직전의 데이터베이스 상태로 복구를 합니다.

 


좀더 심화적으로 해봅시다.

 

순서대로 계좌이체트랜잭션T1이 실행되고 계좌이체트랜잭션 T2가 실행되었다면

로그파일에도 꾸준히 변경연산이 진행될때마다 기록을 할 것 입니다. 만약 T2에 <T2,commit> 바로 이전에 장애가 발생했

다면 T1은 redo연산 T2는 undo연산을 실행합니다.

그리고 이렇게 redo와 undo연산이 두번 있을 때의 경우엔 undo연산을 먼저 실행한후 redo연산을 진행합니다.

그리고 redo연산이 여러개인 경우엔 로그에 기록된 순서대로 redo연산을 실행하고 undo연산이 여러개인 경우에는 로그에 기록된 순서의 반대로 undo연산을 실행합니다.

 


-지연 갱신 회복 기법

 

트랜잭션이 수행되는 동안에는 데이터 변경 연산의 결과를 데이터베이스에 즉시 반영하지 않고 로그파일에만 기록해두었다가 트랜잭션이 부분 완료된 후에 로그에 기록된 내용을 이용해 데이터베이스에 한 번에 반영하는 기법입니다.

이렇게 하면 장점은 트랜잭션이 수행되는 동안 장애가 발생한 경우 어차피 디스크에는 아무런 저장을 하지 않았으니 데이터베이스를 원래 상태로 되돌릴 필요가 없습니다.그냥 로그파일을 버리면 되니 즉 undo연산이 필요없어졌습니다.

undo연산이 필요가 없어졌으니 로그파일에는 이전값을 저장할 필요도 없어졌습니다. 

변경 연산 실행에 대한 레코드는 <Ti,X,new_value>형식으로 기록하고 redo연산만을 수행하면 됩니다


 

로그 회복 기법 이외에도 검사 시점 회복 기법, 미디어 회복 기법이 있습니다. 

 

다음 시간에는 트랜잭션이 여러개 수행할 때 제어하는 병행 제어에 대해 알아보겠습니다. 감사합니다