본문 바로가기
스터디/오라클 성능고도화 원리와 해법1

CH1.오라클 아키텍처 - 4.Redo , 5. Undo

by 취미툰 2019. 12. 19.
반응형

4.Redo
Redo?
오라클은 데이터파일과 컨트롤파일에 가해지는 모든 변경사항을 하나의 Redo 로그 엔트리로써 Redo 로그에 기록합니다.
Redo 로그는 Online Redo log와 Archive redo log로 구성됩니다.
Online Redo 로그는 redo 로그 버퍼에 버퍼링된 로그 엔트리를 기록하는 파일로써 최소 두 개 이상의 파일로 구성됩니다.
현재 사용 중인 redo 로그 파일이 가득차면 다음 redo 로그 파일로 로그 스위칭이 발생하며, 모든 로그 파일이 가득차면 다시 첫번째 redo 로그 파일부터 재사용하는 라운드 로빈 방식으로 운영되고 있습니다.
Archived redo 로그는 online redo 로그가 재사용되기 전에 다른 위치로 백업해둔 파일을 말합니다. archive log mode에서만 사용가능합니다.

목적?
1)Database Recovery(media recovery)
물리적으로 디스크가 깨지는 Media fail 장애 발생 시 데이터베이스 복구를 위해 사용되며 이때 archive log를 이용하게 됩니다.

2)Cache Recovery(instance recovery 단계에서 roll forward 단계)
I/O성능을 향상시키기 위해 버퍼캐시를 사용하며, 캐시에 저장된 변경사항이 디스크 상의 데이터 블록에 아직 기록되지 않은 상태에서 정전 등이 발생해 인스턴스가 비정상적으로 종료하게 되면 버퍼캐시에 있던 작업내용을 모두 잃게 됩니다. 이러한 instance crash 발생 후 시스템을 재기동하면 우선 online redo 로그에 기록된 기록사항들을 읽어들여 마지막 체크포인트이후부터 사고 발생 직전까지 수행되었던 트랜잭션들을 재현합니다. (commit,uncommit상관없이 last checkpoint까지의 변경사항들을 셧다운 이전상태로 복구하는 것을 roll forward라고 합니다) cache recovery가 완료되면 Undo데이터를 이용해 셧다운되는 시점에 커밋되지 않았던 트랜잭션들을 모두 롤백하는 Transaction Recovery가 진행됩니다.(이 과정을 roll back이라고 합니다) roll forward와 roll back이 모두 완료되고 나면 커밋되지 않은 기록사항들은 모두 제거되어 데이터베이스는 커밋에 성공한 데이터만 남게되며 완전히 동기화 된 상태가 됩니다.

3)Fast commit
변경된 메모리 버퍼 블록을 디스크 상의 데이터 블록에 기록하는 작업은 random 액세스 방식으로 이루어지기 때문에 느립니다. 반면 redo 로그는 append방식으로 기록하므로 상대적으로 빠릅니다. 따라서 트랜잭션 발생 시 건건이 데이터 파일에 기록하기보다 우선 변경사항을 append방식으로 빠르게 로그 파일에 기록하고 메모리 데이터 블록과 데이터 파일간의 동기화는 적절한수단(dbwr,checkpoint)을 이용해 나중에 배치방식으로 일괄 수행합니다,
사용자의 갱신내용이 메모리상의 버퍼블록에만 기록된 채 아직 디스크에 기록되지 않았지만 redo 로그를 믿고 빠르게 커밋을 완료한다는 뜻에서 fast commit이라고 부릅니다.
다른 dbms와 다르게 오라클만의 fast commit을 구현하기위한 특징적인 기능이 있는데 Delayed 블록 클린아웃입니다. 완전한 커밋을 위해서는 lock을 해제하는 일까지 완료해야 하는데, 오라클은 별도의 lock 매니저 없이 레코드의 속성으로 로우 lock을 구현했기 때문에 lock을 해제하려면 갱신했던 블록들을 일일이 찾아다녀야합니다.(다른 dbms는 lock 매니저가 있어서 로우 lock을 관리하고 커밋시점에 빠르게 lock리소스를 제거할 수 있습니다)
따라서 redo 로그에 기록하는 것만으로는 도저히 커밋을 빠르게 처리할 수 없는 구조여서 Delayed 블록 클린아웃이라는 추가적인 매커니즘을 사용합니다.
커밋시점에는 Undo 세그먼트 헤더의 트랜잭션 테이블에만 커밋 정보를 기록하고, 블록 클린아웃(갱신된 블록에 커밋 정보를 기록하고 lock을 해제하는 작업)은 나중에 수행하는 것을 말합니다.

redo 레코드를 기록할때는 redo 로그 버퍼에 기록한 후 일정조건을 만족하면 lgwr에 의해 redo 로그 버퍼의 내용을 redo 로그 파일에 기록하는 것입니다.
조건은 아래와 같습니다.
- 3초마다
- DBWR프로세스로 부터 신호를 받을때(데이터 파일에 변경사항을 쓰기 전에 로그버퍼 내용을 redo 로그 파일에 기록하도록 LGWR에게 신호를 보냄)
- 로그 버퍼의 1/3이 차거나 redo 레코드 양이 1MB가 넘을때
- 사용자가 커밋 또는 롤백을 날릴 때
fast commit의 핵심은 마지막에 있는 커밋또는 롤백명령을 날릴때라고 할 수 있는데, 트랜잭션이 영속성을 보장받으려면 최소한 커밋시점에는 redo 정보가 메모리가 아닌 디스크 상에 안전하게 저장되었음이 확인되어야 합니다.
또 DBWR이 dirty buffer를 데이터파일에 기록하기 전에 로그 버퍼를 먼저 redo 로그에 기록하는 이유는 무엇일까? instance crash발생 시 redo 로그에 기록된 내용을 재현해 캐시 블록을 복구하고 최종적으로 커밋되지 않은 트랜잭션은 롤백하게 되는데, redo 로그에는 없는 변경사항이 이미 데이터파일에 기록되어 있으면 사용자가 최종 커밋하지 않은 트랜잭션이 커밋되는 결과를 초래하기 때문입니다.

아래는 fast commit매커니즘을 정리한 그림입니다.

사용자가 커밋을 날리면 -> 서버프로세스는 커밋레코드를 redo 로그 버퍼에 기록하고 -> lgwr은 이것을 즉시 트랜잭션 로그 앤트리와 함께 redo 로그 파일이 저장하고나서 -> 커밋을 수행한 트랜잭션에 success code를 리턴합니다. (그림에서 4번까지의 상황) 여기까지 완료되면 갱신내용이 메모리 버퍼에만 기록된 채 디스크에는 기록되지 않았지만 instance crash가 발생하더라도 redo로그를 이용해 언제든지 복구가 가능한 상황이 되었으므로 안심하고 커밋을 완료할 수 있는것입니다.
사용자가 커밋 또는 롤백할 때마다 log file sync 대기이벤트가 발생하는 것을 볼 수 있는데 이것은 lgwr프로세스가 로그 버퍼 내용을 redo 로그 파일에 기록할 때까지 서버 프로세스가 대기하는 현상때문에 발생합니다.

5.UNDO
구조적으로 테이블세그먼트와 별반 다르지 않습니다. 테이블 세그먼트와 마찬가지로 익스텐트단위로 확장하고 빠른 읽기/쓰기를 위해 undo 블록들을 버퍼 캐시에 캐싱하며, 데이터 유실을 방지하기 위해 그 변경사항을 redo 로그에 로깅하는 점도 같습니다.
다른점이라면 세그먼트에 저장하는 내용인데 각 트랜잭션 별로 undo 세그먼트를 할당해주고 (두 개이상의 트랜잭션이 하나의 undo 세그먼트를 할당받아 같이 사용할 수 있음) 그 트랜잭션이 발생시킨 테이블과 인덱스에 대한 변경사항을 undo 레코드 단위로 undo 세그먼트 블록에 차곡차곡 기록합니다.
(AUM[Automatic Undo Management]이 9i부터 도입되어 현재까지 사용되고 있으며, undo 세그먼트마다 하나의 트랜잭션이 할당되는 것을 목표로 세그먼트 개수를 오라클이 자동 관리합니다. 트랜잭션에 독립적으로 할당해 줄 undo 세그먼트가 없을때(online으로 전환할 수 있는 offline세그먼트가 없고 새로운 undo세그먼트를 생성할 공간도 부족할 때) 가장 적게 사용되는 undo세그먼트 중 하나를 할당합니다.
AUM에서는 다른 undo 세그먼트로부터 free undo space를 가져올 수있으며(dynamic extent transfer), undo 테이블스페이스 내에 있는 모든 undo space를 소진했을때 ORA-01562를 발생시킵니다)

목적?
1) transaction rollback
트랜잭션에 의한 변경사항을 커밋하지 않고 롤백할때 undo 데이터를 사용합니다.

2) transaction recovery (instance recovery시 rollback 단계)
instance crash 발생 후 redo를 이용해 roll forward단계가 완료되면 최종 커밋되지 않은 변경사항까지 모두 복구합니다. 이때 커밋되지 않은 트랜잭션은 모두 롤백해야 하는데 이때 undo 데이터를 사용합니다

3) read consistency
오라클은 undo를 사용하여 읽기 일관성을 유지합니다.(다른 dbms는 lock을 통해 읽기 일관성을 유지함) 이것은 아래에서 자세하게 다루겠습니다.

(1) Undo 세그먼트 트랜잭션 테이블
Undo Segment 중 첫번째 익스텐트, 그중 첫번째 블록에는 undo 세그먼트 헤더 정보가 담깁니다. 그곳에는 트랜잭션 테이블 슬롯이 위치하는데 각 슬롯에 기록되는 사항들은 아래와 같습니다.
- 트랜잭션 ID [ USN(Undo Segment Number)# + Slot# + Wrap#]
- 트랜잭션 상태정보
- 커밋 SCN
- Last UBA(Undo Block Address)
- 기타
트랜잭션을 시작하려면 먼저 Undo 세그먼트에 있는 트랜잭션 테이블로부터 슬롯을 할당받아야 하며, 할당받은 슬롯에 자신이 현재 Active상태임을 표시하고서 갱신을 시작합니다. 참고로, 트랜잭션 슬롯을 곧바로 얻지 못해 이용 가능한 슬롯이 생기기를 기다릴 때 발생하는 대기 이벤트가 undo segment tx slot 입니다.
이제부터, 트랜잭션이 발생시키는 데이터 또는 인덱스 블록에 대한 변경사항은 Undo블록에 Undo 레코드로써 하나씩 차례대로 기록합니다.
(INSERT - 추가된 레코드의 rowid, UPDATE - 변경되는 컬럼에 대한 before image, DELETE - 지워지는 로우의 모든 컬럼에 대한 before image)
last UBA는 트랜잭션의 기록사항들을 가장 마지막 UNDO 레코드 뒤에 계속 추가해 나가려고 유지하는 일종의 포인트입니다. 그리고 각 UNDO 레코드 간에는 체인 형태로 연결되어 있어 데이터를 롤백하고자 할때 이 체인을 따라 거슬러 올라가며 작업을 수행하게 됩니다. 하나의 undo 블록에 쓰기가 완료되면 새로운 undo블록을 할당받아 쓰기 작업을 계속해 나갑니다.
v$transaction 뷰에 있는 used_ublk와 used_urec컬럼을 통해 현재 사용중인 undo 블록 개수와 현재까지 기록한 undo 레코드 양을 확인할 수 있습니다.
아직 커밋되지 않은 Active 상태의 트랜잭션이 사용하는 undo 블록과 트랜잭션 테이블 슬롯은 절대 재사용되지 않습니다. 사용자가 커밋해 트랜잭션이 완료되면 트랜잭션 상태정보를 committed로 변경하고 그 시점의 커밋 SCN을 트랜잭션 슬롯에 저장해 둡니다. 이제 이 트랜잭션 슬롯과 undo 블록들은 다른 트랜잭션에 의해 재사용될 수 있습니다. 하지만 가장 먼저 커밋된 트랜잭션 슬롯부터 순차적으로 재사용되기 때문에 undo 데이터는 커밋 후에도 상당기간 남아있게 됩니다.

undo retention - 트랜잭션이 완료되었어도 지정한 시간 동안은 가급적 undo 데이터를 재사용하지말라고 기간을 정해두는 것.
retention guarantee - undo retention으로 지정된 시간 이내에 커밋된 undo 정보는 무조건 재사용하지 않음

(2) 블록 헤더 ITL 슬롯
undo 세그먼트 헤더에는 트랜잭션 테이블 슬롯이 있다면 각 데이터 블록과 인덱스 블록헤더에는 ITL(interested transaction lsit)슬롯이 있습니다. 아래와 같은 내용이 기록됩니다.
- ITL 슬롯 번호
- 트랜잭션 ID
- UBA
- 커밋 flag
- locking 정보
- 커밋 SCN(트랜잭션이 커밋된 경우)

트랜잭션을 시작하려면 undo 세그먼트 트랜잭션 테이블로부터 슬롯을 먼저 확보하듯이, 특정 블록에 속한 레코드를 갱신하려면 먼저 블록 헤더로부터 ITL 슬롯을 확보해야 합니다. 거기에 트랜잭션 ID를 기록하고 현재 해당 트랜잭션이 Active상태임을 표시한 후에 블록 갱신이 가능합니다. ITL 슬롯을 할당받지 못하면 트랜잭션은 계속 진행하지 못하고 블로킹되었다가 해당 블록을 갱신하던 앞선 트랜잭션 중 하나가 완료(커밋 또는 롤백)될 때 비로소 ITL 슬롯을 얻어 작업을 계속해서 진행할 수 있게됩니다.
오라클은 ITL 슬롯이 부족해 트랜잭션이 블로킹되는 현상을 최소화할 수 있도록 3가지 옵션을 제공합니다. 테이블과 인덱스를 생성할때 initrans,maxtrans,pctfree파라미터입니다.
initrans - 블록을 사용하려고 처음 포맷할 때 블록헤더에 ITL 슬롯을 몇 개 할당할지를 결정하는 파라미터
maxtrans - 지정된 개수 만큼 데이터 영역에 추가 ITL 슬롯을 할당할 수 있음
pctfree - 예약된 공간이 update(인덱스는 insert)에 의해 모두 사용되고 없다면 ITL을 할당받지 못해 경합이 발생하게 됩니다.
ITL 슬롯이 부족할때 발생하는 대기이벤트는 enq: TX - allcate ITL entry 입니다.

(3) Lock Byte
오라클은 레코드가 저장되는 로우마다 그 헤더에 lock byte를 할당해 해당 로우를 갱신 중인 트랜잭션의 ITL 슬롯 번호를 기록해둡니다. 이것이 로우 레벨 lock이며, 오라클은 로우레벨 lock과 트랜잭션 lock을 조합해서 로우 lock을 구현하였습니다. 요컨대, 레코드를 갱신하려고 할때 대상 레코드의 lock byte가 활성화 되어 있으면 ITL슬롯을 찾아가고 다시 그 ITL 슬롯이 가리키는 트랜잭션 테이블 슬롯을 찾아가 그 트랜잭션이 아직 active 상태면 트랜잭션이 완료될 때까지 대기(트랜잭션 lock)합니다.

ITL 슬롯에 기록되는 내용 중 UBA가 있습니다. 이는 트랜잭션에 의해 발생한 변경 이전 데이터(before image)가 저장된 undo 블록 주소를 가리키는 포인터정보입니다. 읽기 일관성을 위해 CR copy를 생성해서 과거버전으로 되돌리려 할 때 사용합니다. 이는 다음절에서 계속해서 설명하겠습니다.

반응형

댓글