Inor

[DB] 트랜잭션 : Log 본문

Computer Engineering/Database

[DB] 트랜잭션 : Log

Inor 2017. 8. 14. 14:20

Log


 대부분의 DMBS는 undo, redo 복구를 위해서 log를 사용 합니다. 로그는 데이터베이스의 복구를 위해서 사용되기 때문에 매우 중요하고 데이터 손실이 있어서는 안되는 데이터 입니다. 그렇기 때문에 몇몇 DBMS에서는 여러개의 로그를 유지하며 안정적으로 사용하기도 합니다. 그러나 대부분의 DBMS에서는 성능상의 이유로 하나의 로그만을 사용 합니다. 로그는 로그 레코드의 연속이며 데이터베이스의 모든 변경 사항을 저장 합니다. 로그 레코드는 이전의 로그 레코드에 덧붙이는 방식으로 저장 됩니다. 로그 레코드는 식별을 위한 식별자를 사용해서 구분 합니다. 아래는 로그 레코드가 기록되는 방식을 보여주는 그림 입니다.





로그 레코드의 오브젝트


  • 물리적 상태 로깅(physical state logging) : 물리적 상태 로깅에서는 상태 변화 이전의 이미지와 현재의 이미지를 모두 로그 레코드로 저장합니다. 변화된 이미지는 redo 복구시에 사용하며 변화 이전의 이미지는 undo 복구시에 사용하는 방식으로 사용 됩니다. 대부분의 DBMS가 채택하고 있는 방식 입니다. 
  • 물리적 전이 로깅(physical transaction logging) :  물리적 전이 로깅에서는 상태 변화 이전과 이후의 이미지를 모두 저장하기 보다는 XOR 차이점을 기록하는 방식입니다. XOR 이미지와 레코드 이미지를 이용해서 undo, redo 복구를 수행 합니다.
  • 논리적 전이 로깅(logical transaction logging) : 논리적 전이 로깅은 이미지 데이터를 저장하는 것이 아니라 연산 자체를 저장 합니다. 예를 들어서 x = x - 1 연산을 수행 했다면 연산 자체를 기록 합니다. undo 복구에서는 해당 연산의 역연산을 수행하고 redo 복구에서는 해당 연산을 다시 연산 합니다. 물리적 로깅보다 레코드의 크기가 작다는 장점이 있습니다. 하지만 더 중요한 점은 연산을 수행하면서 레코드의 페이지 내 주소값이 바뀌거나 페이지 자체가 변하는 경우에는 물리적인 복구가 쉽지 않은데 논리적인 복구를 통해서 더 간단하게 복구가 가능 하다는 점 입니다.

 DBMS에서 로그 레코드를 기록할때 하나의 방법만을 선택해서 사용하는 것이 아니라 여러가지 방법들을 적절한 상황에 맞게 혼용해서 사용 합니다. 로그 레코드를 복구할때 가장 중요한 것 중에 하나는 멱등성이 보장되야 한다는 점 입니다. 여러번 수행하도 한번 수행한 것과 같은 결과를 가져야 합니다. 물리적으로 저장된 레코드에 대해서는 멱등성이 쉽게 보장 되지만 논리적인 경우에는 조금 복잡할 수 있습니다.




로그 작성


 로그의 경우에는 로그의 데이터 타입에 관계 없이 두 가지 큰 원칙을 따라서 작성 됩니다. 첫째, 로그는 데이터베이스에 업데이트 되기 전에 기록 되야 합니다. undo 연산을 하기 위해서는 수정된 내용들이 데이터베이스에 업데이트 되기 전에 기록되야 합니다. 이것을 WAL(Write Ahead Logging)이라고 부릅니다. 둘째, 적어도 커밋이 되기 전에는 로그 레코드가 기록되야 합니다. 트랜잭션이 정상적으로 종료되기 위해서는 redo 복구를 위해서 로그가 기록 돼야 합니다. DBMS는 로그를 기록하기 위해서 로그 버퍼를 사용 합니다. 로그 버퍼는 DBMS마다 다르게 구현이 되는데 로그 버퍼를 이용해서 로그 레코드를 작성 한다는 점은 동일 합니다.

 로그를 작성하는 일은 많은 연산을 요구하는 일이고 시간을 많이 사용하는 작업 입니다. 로그를 작성하기 위해서는 write와 fsync 함수를 호출 하는데, 두개의 함수 중에서 fsync 함수 호출은 매우 느린 연산이고 커밋을 위해서는 트랜잭션의 로그가 로그 파일에 써져야 하기 때문에 fsync 함수가 종료 할때 까지  대기 합니다. 커밋 연산이 사용하는 대부분의 시간은 로그 파일에 로그 레코드를 쓰고 fsync 함수를 호출하는 시간 입니다.




Group Commit


 커밋 하는 시간을 줄이기 위한 방법 중 하나가 그룹 커밋 입니다. 트랜잭션은 병렬적으로 이루어지는 것 같은 방식으로 진행이 되기 때문에 하나의 트랜잭션이 커밋 요청을 하고 곧 이어서 다른 트랜잭션의 커밋 요청이 올 것 입니다. 이런 커밋 요청들을 한번에 하나씩 처리하게 되면 fsync 함수를 여러번 호출해야 하고 로그 레코드를 로그 파일에 쓰는 시간이 길어질 것이며 결과적으로 커밋 요청을 처리하는 시간이 증가 합니다. 이런 과정이 지속되면 데이터베이스가 작업을 처리하는 전체 시간 또한 증가하게 되고 성능의 문제에 직면하게 됩니다. 그렇기 때문에 트랜잭션이 커밋 요청을 하면 즉각적으로 요청을 수행하는 것이 아니라 다른 트랜잭션들의 커밋 요청도 기다립니다. 그렇게 되면 한번에 여러개의 커밋 요청을 수행하게 되고 커밋 요청을 하나씩 처리할때 보다 로그 레코드를 로그 파일에 입력하는 시간이 줄어들게 됩니다.

 그렇다면 커밋 요청을 기다리는 시간이 증가 할수록 성능이 좋아질까요? 커밋 요청을 기다리는 시간을 정하는 것은 매우 중요한데 그 이유는 커밋 요청을 기다리는 시간을 너무 많이 증가시켜 버리면 응답 시간은 늘어나고 효율은 정체되는 현상이 발생 합니다. 또한, 너무 짧으면 효율성이 낮아 집니다. 그렇기 때문에 적절한 대기 시간을 설정하는 것이 전체적인 데이터베이스의 성능을 높이는데 매우 중요합니다.




참고



'Computer Engineering > Database' 카테고리의 다른 글

[DB] 정규화  (0) 2017.08.14
[DB] 트랜잭션 : Recovery(회복)  (0) 2017.08.14
[DB] 트랜잭션 : undo, redo  (0) 2017.08.13
[DB] 트랜잭션(Transaction)  (0) 2017.08.11
Comments