ORA-1555 Snapshot Too Old 에러가 발생하는 원인은 여러가지가 있을 수 있다.
이를 이해하기 위해서 오라클의 내부 메카니즘에 대해서 알아볼 필요가 있다. 오라클은 항상 Statement-Level Read Consistency를 유지한다. 즉, 하나의 Query 에서 읽어들이는 값은 그 Query가 발생한 시점의 값, 즉 Snapshot 을 반영한다. 따라서 Query 가 진행되는 동안 데이타가 변하더라도 Query의 결과값에는 아무런 영향을 끼치지 않는다.
오라클은 특정한 시점을 SCN(System Change Number)로 관리한다. 달리 말하면 SCN은 특정한 시점에서 데이타베이스의 상태라고도 할 수 있다. Read Consistency를 유지하기 위하여 Query가 실행되는 순간 해당 시점의 SCN이 세팅된다. 어떠한 Query든 조회 시점 이후의 SCN 값을 갖는 데이타를 읽어들여 서는 안된다.
Read Consistent Snapshot을 구현하기 위한 방법으로 언두 세그먼트가 사용된다. 데이타가 변경되면 변경되기 이전의 값은 언두 세그먼트에 기록되고 데이타 블럭 헤더에는 변경되기 이전 값이 기록된 언두 세그먼트 블럭의 위치가 기록된다. 모든 데이타 블럭에는 가장 최근의 커밋 시점의 SCN이 기록되어 있다. Query 가 진행되면 데이타 블럭에서는 Query의 SCN 보다 이전의 SCN값을 갖는 블럭만이 읽혀지고 그 이후의 SCN 값을 갖는 블럭과 커밋되지 않은 값을 갖는 블럭에 대한 Query는 언두 세그먼트에서 읽혀지게 된다. 이 때 만약 언두 세그먼트에 원하는 블럭이 없어서 읽어들이 못하게 되면 ORA-1555 에러가 발생하게 된다.
언두 세그먼트는 트랜잭션이 끝나기 전까지 변경된 데이타의 원래 상태를 기록하고 있다. 그런데 트랜잭션이 종료되면 언두 세그먼트에 의해 점유된 영역은 Free 상태로 되면서 다른 트랜잭션에 의해서 덮어씌워질 도 있게 된다. 따라서 Query가 필요로하는 값을 가진 언두 세그먼트 블럭이 더 이상 존재하지 않는 상황에서 그 블럭을 요구하게 되면 ORA-1555 에러가 발생하게 되는 것이다.
ORA-1555 에러가 발생하는 경우
1. 데이타의 변경이 심한 데이타베이스에서 언두 세그먼트의 갯수와 크기가 작을 경우에 발생한다. 많은 트랜잭션이 데이타를 자주 변경하고 커밋하게 되면 커밋된 트랜잭션이 이용하던 언두 세그먼트 공간을 다른 트랜잭션이 이용하게 될 가능성이 많아진다.
따라서 긴 Query의 경우 원하는 값을 언두 세그먼트에서 얻고자 할 때 이미 다른 트랜잭션이 그 값이 저장된 공간을 이용해 버리는 결과가 발생할 수 있다. 이와같은 경우에는 크기가 큰 언두 세그먼트를 이용하면 어느정도 예방이 가능하다.
2. 언두 세그먼트가 손상되어 읽을 수 없게 된 경우
3. Fetch Across Commit
한 테이블에 대하여 Query가 커서를 열고 루프 내에서 데이타를 Fetch하고 변경하고 커밋하는 과정에서 발생한다. 이 경우에는 ORA-1555 에러가 자주 발생할 소지가 있는데 예를 들어 커서가 SCN=10 에 Open되었다고 하자. 따라서 이 커서에 관련된 Fetch는 SCN<=10 인 블럭만을 읽어들여야 한다. 이
프로그램은 데이타를 Fetch 한 후에 변경하고 다시 커밋하는 과정을 계속 반복하는데 SCN=20에서 커밋했다고 하자. 만약 이후의 Query가 이전의 커밋된 블럭의 데이타를 요구할 경우 그 값이 이미 변경되었으므로 언두 세그먼트를 검색 하지만 많은 변경이 있어왔기 때문에 SCN=10인 블럭을 찾지 못하고 ORA-1555 에러를 유발할 수 있다. 이를 방지하기 위해서는 커서가 Open된 상태에서는 커밋을 자주하지 않고 언두 세그먼트 크기를 키워 나가도록 한다.
4. Fetch Across Commit 와 Delayed Block Clean Out
Delayed Block Clean Out 도 이 에러를 유발할 수 있다. 데이타 블럭이 변경되고 커밋되면 오라클은 언두 세그먼트 헤더에 그 트랜잭션이 커밋되었다고 기록하지만 데이타 블럭을 바로 변경하지는 않는다 (Fast Commit). 그리고 다음 트랜잭션이 변경된 블럭을 요구할 때야 비로소 변경 시키는데 이를 Delayed Block Clean Out 이라고 한다.
예를 들어 3 과 같은 경우에서 두개의 테이블에 대한 Query를 알아보자.
즉, 하나의 테이블로부터 데이타를 Fetch 하고 다른 테이블의 데이타를 변경한다고 하자. 데이타가 한쪽 테이블에서 커밋되고 있지만 데이타를 가져오는 테이블에 대해서는 Clean Out이 이루어지지 않았기 때문에 ORA-1555 에러가 발생할 수 있다. 이 문제는 커서를 사용하기 전에 Full Table Scan을
해주면 예방이 가능하다. 커서가 오픈된 상 에서의 커밋은 ANSI 표준에는 들어있지 않지만 오라클에서는 지원이 된다. 하지만 ORA-1555 에러를 일으킬 수도 있다는 점에 유의하여야 한다.