Contents
Snapshot too old
- ORA-01555 (Snapshot too old)
- 데이터를 읽어내려가다가 쿼리SCN 이후에 변경 된 블록을 만나 과거시점으로 롤백한 "Read Consistent" 이미지를 얻으려고 하는데,
Undo블록이 다른 트랜잭션에 의해 이미 재사용되어 필요한 Undo정보를 얻을 수 없는 경우
- 프로그램 코딩 패턴에 문제가 없다면, Undo세그먼트가 너무 작다는 신호이다.
Undo 실패
!이미지 65.jpg!
- SCN 123 시점에서 이 쿼리가 시작되었다.
- 쿼리가 진행되는 동안 은행으로부터 고객의 입금내역을 전송받아 일괄 처리하는 배치 프로그램이 수행되었는데,
프로그램은 각 건별로 커밋하도록 작성되었다.( 루프문안에 COMMIT; )
이 프로그램이 돌기 시작한지 얼마 지나지 않아 다른 트랜잭션에 의해 H 고객의 DEP 100 에서 200 으로 변경되고 나서 커밋되었다.
이때, 블록 SCN은 129로 변경되었다.
- 시간이 흘러 H 고객의 DEP 변경내역( 100 \-> 200 )을 담은 Undo블록이 다른 트랜잭션에 의해 재사용되었다.
- 위 쿼리를 수행 중인 프로세스가 홍길동 고객의 수납액이 담긴 블록에 도달했을 때,
블록 SCN이 자신의 쿼리 SCN 123보다 큰 129임을 확인하고, 변경된 UNDO레코드를 찾으려고, ITL 엔트리에 기록된 UBA를 읽어 Undo 세그먼트 블록을 찾아간다.
- 하지만 Undo블록은 이미 다른 트랜잭션에 의해 재사용된 상태이므로 ORA-01555에러를 발생시키며 진행을 멈춘다.
Featch across commit
!이미지 68.jpg!
- 명시적으로 커서를 열어 로우 하나씩 Fetch (Instance 에서 구한 값을 User process 로 넘겨주는일) 하면서 값을 변경하고
루프 내에서 계속해서 커밋을 날리는 방식.
- ANSI 표준에도 불구하고 Oracle 은 사용자가 Fetch across commit 을 할수 있도록 허용.
- 에러 발생 경우를 인지하고 프로그램 작성이 필요함.
Block 클린아웃 실패
!이미지 71.jpg!
- 대량 업데이트 후에 커밋된 트랜잭션은 변경했던 블록들은 모두 클린아웃하지 않은 상태에서 자신이 사용하던 트랜잭션 테이블 슬롯은 Free( 9 ) 상태로 변경하고 트랜잭션을 완료한다.
이때부터 트랜잭션 테이블 슬롯은 다른 트랜잭션에 의해 재사용 될 수 있다.
- 시간이 흘러 그 변경된 블록들을 읽어야 하는 시점에 하는 시점에 Delayed 블록 클린아웃을 위해 트랜잭션 테이블 슬롯을 찾아갔는데, 해당 슬롯이 다른 트랜잭션에 의해 이미 재사용되고
없다면 Undo 세그먼트 헤더 블록에 가해진 변경사항을 롤백하려고 시도한다. 다행이 찾고자 하는 트랜잭션에 대한 커밋 정보가 언두 블록( 레코드 ) 에 남아 있다면 현재 읽고자하는
블럭을 정확한 커밋 SCN을 가지고 블록 클린아웃을 수행한다.
- 커밋 정보가 언두 블록 에 남아 있지 않을경우 트랜잭션 슬롯이 필요해지면 커밋 SCN이 가장 낮은 트랜잭션 슬롯 부터 재사용하는데,
그 슬롯에 기록돼 있던 커밋 SCN을 UNDO 세그먼트 헤더에 '최저 커밋 SCN'으로서 기록해둔다.
- 트랜잭션 슬롯이 재사용되고 나면 그 슬롯을 사용하던 이전 트랜잭션의 정확한 SCN을 확인하는 것이 불가능해 지지만 UNDO 세그먼트 헤더에 기록돼 있는 '최저 커밋 SCN'이전에
커밋 되었다는 사실만큼은 짐작할 수 있다.
- 따라서 아직 클린아웃되지 않은 블록을 큰린하려고 ITL이 가르키는 트랜잭션 테이블 슬롯을 찾아갔을 때 커밋정보가 이미 지워지고 없으면, UNDO 세그먼트 헤더에 있는
'최저 커밋 SCN'을 블록 ITL 엔트리에 커밋 SCN으로서 기록( 추정된 커밋 SCN임을 표기하기 위해 ITL 슬로 커밋 FLAG에 C--\- 대신 C-U-라고 기록) 함으로써 블록 클린아웃을
마무리하고, 블록 SCN도 변경한다.
- 문제는 클린아웃 시점에 일관성 모드 일기가 가능한지 여부일 텐데, 쿼리가 진행되는 동안에 많은 트랜잭션이 한꺼번에 몰리지만 않는다면 '최저 커밋 SCN'이 갑자기
많이 증가하지는 않을 것이므로 '최저 커밋 SCN'에 의해 추정된 블록 SCN은 대개 쿼리 SCN보다 작다.
따라서 쿼리가 시작된 이후에 해당 블록에 변경이 가해지지 않았음이 확인되므로 정상적인 일관성 모드 읽기도 가능하다.
Snapshot too old 회피 방법
- fetch across commit 형태의 프로그램 작성을 피해 다른 방식으로 구현한다. 다른 방식으로 구현하기 어렵다면 COMMIT 횟수를 줄인다.
- 트랜잭션이 몰리는 시간때는 오래 걸리는 쿼리가 같이 수행되지 않도록 시간ㅇ르 조정한다.
- 큰 테이블을 일정 범위로 나누어 읽고 단계적으로 실행할 수 있도록 코딩한다.
- 오랜 시간에 걸쳐 같은 블록을 여러번 방문하는 NL 형태의 조인문 또는 인덱스를 경유한 테이블 액세스를 수반하는 프로그램이 있는지 체크하고,
이를 회피 할 수 있는 방법( 조인 메소드 변경, FTS )을 찾는다.
- 소트 부하를 감수하더라도 ORDER BY 등을 강제로 삽입해 소트연산이 발생하도록 한다. 많은 데이터를 오랜 시간에 걸쳐 Fetch 하는 동안
Undo 정보를 지속적으로 참조하기 때문에 발생하는 것이므로 서버 내에서 빠르게 데이터를 읽어 템프 세그먼트에 저장하는 데에만 성공하면
이후에는 같은 블록을 아무리 재방문하더라도 더는 에러가 발생할까 걱정하지 않아도 된다.
- 만약 delayed 블록 클린아웃에 의해 Snapshot too old가 발생하는 것으로 의심되면
대용량 업데이트 후 곧 바로 해당 테이블에 대해 Full Scan 하도록 쿼리를 날리는 것도 하나의 해결방법이 될 수 있다. ( 테이블 풀스캔 )
- 만약 인덱스 블록에 문제가 발생한다고 판단된다면 인덱스 리프 블록을 모두 스캔하도록 한다. ( 인덱스 플스캔 )
참고
http://wiki.gurubee.net/display/STUDY/Snapshot+too+old