데이터 블록 방문 및 언두

  • 세션은 데이터 블록을 읽을 때마다, 지금 보여지는 것이 적절한 데이터 버전인지를 보장할 필요가 있다.
  • 세션은 uncommit된 데이터, 또는 쿼리 시작 이후에 commit된 데이터를 보면 안 된다. (read-consistent version이라고 부른다.)

create table t1(id number, n1 number);

insert into t1 values(1,1);
insert into t1 values(2,2);
insert into t1 values(3,3);

commit;

execute dump_table_block('t1');

Block header dump:  0x0040fad2
 Object id on Block? Y
 seg/obj: 0x4aa15  csc: 0x76c.7f167cb3  itc: 2  flg: O  typ: 1 - DATA
     fsl: 0  fnx: 0x0 ver: 0x01
---------------------------------------------------------------------------------------
 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x000d.002.000306a3  0x008015b2.74bd.1f  --U-    3  fsc 0x0000.7f167cb4
0x02   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000
-------------> 책과 다름 --------------------------------------------------------------
-- 책 내용 복사
 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x0001.001.00001de4  0x01802ec8.0543.05  --U-    3  fsc 0x0000.01731c46
0x02   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000

data_block_dump,data header at 0x1881085c
===============
tsiz: 0x1fa0
hsiz: 0x18
pbl: 0x1881085c
bdba: 0x0040fad2
     76543210
flag=--------
ntab=1
nrow=3
frre=-1
fsbo=0x18
fseo=0x1f85
avsp=0x1f6d
tosp=0x1f6d
0xe:pti[0]      nrow=3  offs=0
0x12:pri[0]     offs=0x1f97
0x14:pri[1]     offs=0x1f8e
0x16:pri[2]     offs=0x1f85
block_row_dump:
tab 0, row 0, @0x1f97
tl: 9 fb: --H-FL-- lb: 0x1  cc: 2
col  0: [ 2]  c1 02
col  1: [ 2]  c1 02
tab 0, row 1, @0x1f8e
tl: 9 fb: --H-FL-- lb: 0x1  cc: 2
col  0: [ 2]  c1 03
col  1: [ 2]  c1 03
tab 0, row 2, @0x1f85
tl: 9 fb: --H-FL-- lb: 0x1  cc: 2
col  0: [ 2]  c1 04
col  1: [ 2]  c1 04
end_of_block_dump

  • ITL엔트리 0x01를 통해 트랜잭션 1.1.1de4(undo segment 1, slot 1, sequence 7,652)가 최근 블록을 변경했다는 사실을 알 수 있다.
  • 해당 트랜잭션은 SCN 0x01731c46 시점에 커밋되었지만, 해당블록은 아직 적절히 클린아웃 되지 않았다.
    (flag가 upper bound commit을 의미하는 --U-이고, 락 카운터(lck)는 3이기 때문)
  • 블록 내의 세 개의 로우 모두 lb:0x01임을 알 수 있다.
    (블록 내의 세개의 로우는 현재 ITL 엔트리 0x01을 사용하는 트랜잭션에 의해 락이 설정되었다는 것을 의미)
  • uba는, 트랜잭션에 의해 적용된 가장 최근의 변경을 원복하는 법은 파일 6, 블록 11,976, 레코드 5에 저장되어 있다는 것을 알려준다.
    (p.55)

SQL> select to_number('01802ec8', 'xxxxxxxx') from dual;

TO_NUMBER('01802EC8','XXXXXXXX')
--------------------------------
                        25177800

SQL>
select dbms_utility.data_block_address_file(25177800) file#,
dbms_utility.data_block_address_block(25177800) block#
from dual;
SQL>   2    3
     FILE#     BLOCK#
---------- ----------
         6      11976

Interrested Transaction List

  • ITL의 초기 개수 : 9i이후 기본 2(이전 기본 1), 데이터 딕셔너리에는 1로 표현.
    오브젝트 생성(또는 리빌드)시 initran 파라미터로 조절.
  • ITL의 엔트리 개수는 필요시에 늘어날 수 있으며, 블록내의 여유 공간 사용.
  • ITL의 크기는 maxtran 파라미터에 의해 제한. hard limit은 오브젝트의 데이터 블록 크기에 따라 좌우.
    명령어 구문상의 제한은 10g부터 무시, 8kb 블록인 경우 hard limit은 169.
  • ITL은 최근에 데이터 블록을 변경한 트랜잭션을 식별하기 위해 존재.
  • 블록내에 ITL을 저장하기 위해서는 공간이 필요하므로, 오라클은 가능한 적은 ITL 엔트리를 유지하려 한다.
  • 인덱스 블록 스플릿이 발생하면, 이전 ITL은 새로운 리프 블록으로 복사 된다.
컬럼명설명
Itl리스트에 대한 인덱스. 이 숫자는 블록 내에 물리적으로 저장되지 않는다. 이 것은 덤프를 수행할 때
(프로그램)코드에 의해 생성되며, 로우에 락을 설정한 트랜잭션을 식별하기 위해 로우의 lock byte(lb:)값으로 사용된다.
Xid이 블록을 변경한 최근 트랜잭션의 transaction id. "언두세그먼트"."언두슬롯"."언두시퀀스번호"
Uba이 블록에 대한 트랜잭션에 의해 생성된 가장 최근 언두 레코드의 주소. "절대블록주소"."블록시퀀스번호"."블록 내의 레코드 번호"
Flag이 트랜잭션의 명확한 상태를 식별할 수 있는 bit 플래그.

: active(Xid 내의 모든 필드가 0인 경우에는 "never existed")
--U-: Upper bound commit("fast commit"수행 중에도 설절)
C---: Commited 및 cleaned out(관련된 모든 lock type들은 0으로 초기화됨)
B-: 인덱스 블록 스플릿에 대한 recursive 트랜잭션과 관련이 있는것으로 추정된다. 필자는 이 flag일때, UBA는 ITL엔트리의
이전 내용을 보유한 레코드를 가리킨다는 설명을 본 적이 있다. 하지만 이것을 확인하기 위한 작업은 수행하지 않을 것이다.
---T: 필자는 이것이 블록 클린아웃 수행 중이라는 설명을 본 적이 있다. 하지만 이것을 확인하기 위한 작업은 수행하지 않을 것이다.
Lck해당 트랜잭션에 의해 이 블록 내에 락이 설정된 로우의 수
Scn/FscFlag에 따라, commit SCN을 의미하거나, 이 트랜잭션이 커밋 되었을 때 가용한 free영역의 크기를 의미(Fsc:Free Space Credit)
  • initrans값은 리프 블록에만 적용.
  • 리프 블록의 첫 번째 ITL은 리프 블록 스플릿을 위해 사용.
  • 각 브랜치 블록(루트블록 포함)은 하나의 ITL 엔트리를 가지며, 블록 스플릿을 위해서만 사용.
  • 빈(또는 충분히 작은) 테이블에 새로운 인덱스를 생성하면, 인덱스의 싱글 블록이 루트(브랜치)와 리프 블록 용도로 사용.
    (두개의 ITL엔트리를 가진다 - 하나는 브랜치 블록, 두번째는 리프 블록 용도로 사용)
  • direct path load방식(CTAS등)을 사용하여 데이터를 적재한다면, 해당 블록들은 초기에 3개의 ITL 엔트리를 가진다.

동시 수행


Session 1:              update t1 set n1 = 101 where id = 1;
Session 2:              update t1 set n1 = 102 where id = 2;
                        commit;
My session:             set transaction read only;  -- isolation level을 read only로 설정
                                                    -- 다른 사용자에 의해 수행된 어떠한 uncommit된 사항, 이 순간 이후로 수행된 어떠한 commit된 변경사항도 볼 수 없다.
Session 3:              update t1 set n1 = 99 where id = 3;
                        commit;
My session:             select id, n1 from t1;      -- (1,1), (2,102), (3,3) 출력

--> 실제 (oracle 10g 10.2.0.4) 테스트 불가
        ID         N1
---------- ----------
         1          1
         2        102
         3         99
         
Block header dump:  0x0040fad2
 Object id on Block? Y
 seg/obj: 0x4aa15  csc: 0x76c.7f167cd7  itc: 2  flg: O  typ: 1 - DATA
     fsl: 0  fnx: 0x0 ver: 0x01

---------------------------------------------------------------------------------------
 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x0003.01c.000382c9  0x008021bf.8695.0b  C---    0  scn 0x076c.7f167cd0
0x02   0x000c.023.0003434b  0x00802a6b.7b56.04  ----    1  fsc 0x0000.00000000
-------------> 책과 다름 --------------------------------------------------------------
-- 책 내용 복사
 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x000a.00e.00001b93  0x01800518.04f5.34  -U--    1  fsc 0x0000.01731c83
0x02   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000

data_block_dump,data header at 0x1881085c
===============
tsiz: 0x1fa0
hsiz: 0x18
pbl: 0x1881085c
bdba: 0x0040fad2
     76543210
flag=--------
ntab=1
nrow=3
frre=-1
fsbo=0x18
fseo=0x1f71
avsp=0x1f6b
tosp=0x1f6b
0xe:pti[0]      nrow=3  offs=0
0x12:pri[0]     offs=0x1f7b
0x14:pri[1]     offs=0x1f71
0x16:pri[2]     offs=0x1f85
block_row_dump:
tab 0, row 0, @0x1f7b
tl: 10 fb: --H-FL-- lb: 0x2  cc: 2
col  0: [ 2]  c1 02
col  1: [ 3]  c2 02 02
tab 0, row 1, @0x1f71
tl: 10 fb: --H-FL-- lb: 0x0  cc: 2
col  0: [ 2]  c1 03
col  1: [ 3]  c2 02 03
tab 0, row 2, @0x1f85
tl: 9 fb: --H-FL-- lb: 0x1  cc: 2
col  0: [ 2]  c1 04
col  1: [ 2]  c1 64
end_of_block_dump

  • 블록 내의 row0과 1의 위치가 이전과 다르다.
  • 로우 길이가 증가했기 때문에 오라클은 변경된 내용을 블록의 free영역에 복사해야 하기 때문
  • row2에 대한 변경은 로우의 길이를 변경하지 않았기 때문에 여전히 동일한 위치이다.
  • 3개의 트랜잭션이 수행되었지만, 여전히 단 2개의 ITL엔트리만이 존재.
    리스트를 확장하기 보다는 commit된 트랜잭션의 ITL 엔트리를 재사용한다.(oldest commit SCN순으로 재사용)
  • ITL엔트리 0x01ㅇ은 세 번째 트랜잭션에 해당되며, commit cleanout의 효과를 보여준다.
    (커밋 클린아웃은 트랜잭션에 의해 변경된 블록의 일부를 대상으로 하므로, 빠르지만 불완전한 클린아웃)
  • Flag는 upper bound commit으로 설정되지만, 락 카운트는 여전히 1로 설정되어 있다.
  • Scn/Fsc의 값은 0x01731c83 이라는 commit SCN을 보여줌에도 컬럼 명은 여전히 fsc로 설정되어 있다.
  • 블록을 보면 row 2의 lock byte(lb:)는 0x1 이다. row 2는 ITL 0x1에 의해 락이 설정 되었다는 것을 의미
  • ITL 엔트리 0x2는 첫번째 트랜잭션에 해당.
    uncommit된 트랜잭션이며, 하나의 로우에 락을 설정 (ITL 내의 Lck = 1)하고,
    row 0 은 ITL 0x2(블록 내의 lb: = 0x2)에 의해 락이 설정되었다는 것을 알 수 있다.

일관성 생성

1. 버퍼 내에 블록을 복사한 후, 다음 4단계를 수행
2. 필요한 경우, commit된 트랜잭션에 대한 클린아웃 수행
3. Uncommit된 트랜잭션으로부터 변경된 내용을 원복
4. 만일 target SCN(트랜잭션 또는 쿼리시작시점의 SCN)보다 큰 commit SCN을 가진 트랜잭션의 변경사항이
존재한다면, 가장 큰 commit SCN을 가진 트랜잭션의 변경사항부터 원복
5. 필요한 경우, 4번 단계를 반복.


-- 책내용 그대로
 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x000a.00e.00001b93  0x01800518.04f5.34  -U--    1  fsc 0x0000.01731c83
0x02   0x0004.00c.00001964  0x018036ad.05ff.3a  ----    1  fsc 0x0000.00000000

  • 읽기 일관성을 위한 단계
    1. ITL 엔트리 0x02에 대한 트랜잭션이 여전히 active인지를 확인
    (언두세그먼트4, 트랜잭션 테이블내의 슬롯 12의 상태를 확인)
    2. 해당 블록에 적용된 변경사항들을 원복하기 위해서, 언두 블록 0x018036ad을 찾고,
    시퀀스 (incarnation)번호가 05ff인지 점검한 후에, 레코드 0x3a를 읽는다.
    3. 해당 레코드는 "테이블0의 로우 0의 컬럼 1은 c1 02(십진수 1에 대한 오라클의 내부 표현)로 변경되어야 한다"고 알려준다.
    4. 이 변경은 로우의 길이에 영향을 미치므로, 오라클은 블록의 free 영영에 로우를 복사하고 변경을 적용한다.
    5. 해당 언두 레코드는 ITL 엔트리를 "전혀 사용하지 않은"상태로 원복하라는 설명도 포함.

-- 5단계 이후 복사본의 ITL
 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x000a.00e.00001b93  0x01800518.04f5.34  -U--    1  fsc 0x0000.01731c83
0x02   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000

6. 이 시점에, uncommit 된 트랜잭션은 더 이상 존재하지 않는다.
오라클은 target SCN과 ITL내의 모든 SCN을 비교한 후, 0x01731c83이 너무 크다고 판단한다.
7. 언두 레코드 0x01800518.04f5.34 를 읽음으로써 트랜잭션 0x000a.00e.00001b93에 의해 변경된 내용을 원복하는 작업을 시작.
8. 이 언두 레코드는 "테이블 0의 로우 2의 컬럼 1은 c1 04(십진수 3)로 변경되어야 한다"는 것을 알려준다.
이것은 로우의 크기에 영향을 미치지 않으므로 동일한 위치에 변경을 적용한다.
9. 해당 언두 레코드는 해당 트랜잭션에 의해 이 블록에 적용된 첫 번째 변경이므로, ITL 0x01 엔트리의 이전 버전을 보유하고 있다.
10. 오라클은 target SCN 보타 큰 SCN이 ITL내에 존재하지 않는다는 것을 확인.
11. 이것이 해당 세션에서 필요로 하는 정확한 읽기 일관성 버전이다.
이시점의 블록 덤프내용은 책 p61 참고

  • 주목할 점은 4번 단계로 인해 row 0의 주소가 이동되었다는 것.

일관성은 이력을 의미하지 않는다.

  • 읽기 일관성을 위해 생성한 블록의 버전은 실제로는 존재하지 않았던 버전이다.
    {tip}
  • 읽기 일관성이 복제된 블록을 "과거 시점"으로 이동시킨다는 것은 일반적인 사실.
  • 실질적으로는 이것보다는 조금 더 미묘한 사실이 있다.
  • commit된 트랜잭션과 uncommit된 트랜잭션들을 동시에 다루어야 할 뿐 아니라, commit cleanout의 부작용을 깔끔히 정리해야 하기 때문
  • 아마도 이러한 점 때문에 RAC에서는 CR(consistent read)상태와 PI(past image)상태 간의 차이를 구분하는 것 같다.
  • PI상태는 과거 시점에 실제로 존재한 블록의 복제본이므로, 인스턴스가 손상된 current 버전을 소유하고 있다면 rolling forward를 위해 사용될 수 있다.
    {tip}

일량

  • CR blocks created : 데이터 블록의 읽기 일관성 복제본을 생성할 때마다 증가
  • data blocks consistent reads - undo records applied : 읽기 일관성 복제본 생성을 위해서 언두 블록으로부터 언두 레코드를 읽을 때마다 증가.
    (consistent changes : 위의 통계와 동일한 의미를 같는 이전 버전의 성능통계)
  • consistent gets - examination
    : 위 작업을 위해 언두 블록을 방문할 때마다 해당 블록과 관련되 cache buffers chains 래치를 획득하며,
    래치를 소유한 상태로 블록을 읽는다. 이렇게 single latch hold 방식으로 블록을 방문할 때마다 증가.
  • no work - consistent read get : 읽기 일관성을 위해 어떠한 작업도 할 필요가 없는 블록을 읽은 횟수를 의미.