C. Commit SCN

Commit record :

트랜잭션 커밋 시에 current SCN을 트랜잭션 테이블 슬롯에 기록하며, 해당 변경에 따른 리두 체인지 백터를 "Commit record"라고한다.

Fast commit :

커밋이 발생하면 헌두 헤더블록의 해당 트랜잭션 테이블 슬롯의 상태를 커밋된 것으로 변경, (Commit record 발생하여 rolling forward는 가능함)

Commit Cleanout :

세션이 데이터 블록을 변경할 때, 변경 블록에 대한 리스트를 메모리에 생성(최대 버퍼캐쉬의 1/10)하고 커밋 시 버퍼캐시내에 존재하는 블록들을 재방문하여 commit flag를 변경(U)하고 commit SCN을 설정한다.

Delayed block cleanout :

트랜잭션 커밋 시 트랜잭션 테이블 슬롯만을 변경한다면, 데이터블록을 정리하는 작업(lock byte 해제, fsc/scn 에 commit SCN설정, commit flag C--로 변경) 은 차후에 다른 세션에 의해 수행한다. (일의 분담을 통한 오버헤드 감소)

commit cleanout의 일량 비교

commit을 다루는 일반적인 logging 전략 :

  • 트랜잭션 테이블 슬롯에 대한 변경은 로깅하며,
  • 데이터블록의 일부의 변경은 로깅하지 않는다.
    (ITL 엔트리에 대한 변경 및 이외 블록 액세스에 대한 로깅 session logical read, db block gets, consistents)
Cleanout 시 block read관련 stat확인

SQL> drop table t1;

테이블이 삭제되었습니다.

SQL>
SQL> create table t1 (
  2     id              number,
  3     small_no        number(5,2),
  4     small_vc        varchar2(10),
  5     padding         varchar2(1000),
  6     constraint t1_pk primary key (id)
  7  )
  8  pctfree 90
  9  pctused 10
 10  ;

테이블이 생성되었습니다.

SQL>
SQL> insert into t1
  2  select
  3     rownum,
  4     1+ trunc(rownum/10),
  5     lpad(rownum,10),
  6     rpad('x',1000)
  7  from
  8     all_objects
  9  where
 10     rownum <= 500
 11  ;

500 개의 행이 만들어졌습니다.

SQL> commit;

커밋이 완료되었습니다.
NAME                                      DIFF    
-----------------------------------------  -------
IMU Redo allocation size                  9436    
bytes received via SQL*Net from client    574     
commit cleanouts successfully completed   501     
commit cleanouts                          501     
bytes sent via SQL*Net to client          369     
redo size                                 140     
IMU undo allocation size                  52      
user calls                                3       
SQL*Net roundtrips to/from client         2       
enqueue releases                          2       
opened cursors cumulative                 2       
parse count (total)                       2       
non-idle wait count                       2       
execute count                             1       
db block gets                             1       
db block gets from cache                  1       
session cursor cache hits                 1       
user commits                              1       
session logical reads                     1       
redo synch writes                         1       
db block changes                          1       
calls to kcmgas                           1       
recursive calls                           1       
messages sent                             1       
redo entries                              1       
session uga memory                        131024

Cleanout 항목은 500 이 나오지만 실제 읽었던 항목은 기록되지 않는다.

buffer cache flush 후 commit 시 cleanout관련 stat확인

SQL> drop table t1;

테이블이 삭제되었습니다.

SQL>
SQL> create table t1 (
  2     id              number,
  3     small_no        number(5,2),
  4     small_vc        varchar2(10),
  5     padding         varchar2(1000),
  6     constraint t1_pk primary key (id)
  7  )
  8  pctfree 90
  9  pctused 10
 10  ;

테이블이 생성되었습니다.

SQL>
SQL> insert into t1
  2  select
  3     rownum,
  4     1+ trunc(rownum/10),
  5     lpad(rownum,10),
  6     rpad('x',1000)
  7  from
  8     all_objects
  9  where
 10     rownum <= 500
 11  ;

500 개의 행이 만들어졌습니다.

SQL> alter system flush buffer_cache;

시스템이 변경되었습니다.

SQL> commit;

커밋이 완료되었습니다.

NAME                                   DIFF
-------------------------------------- ----
IMU Redo allocation size               9436
bytes received via SQL*Net from client 574 
bytes sent via SQL*Net to client       369 
redo size                              140 
commit cleanout failures: block lost   101 
commit cleanouts                       101 
IMU undo allocation size               52  
non-idle wait count                    4   
user calls                             3   
opened cursors cumulative              2   
SQL*Net roundtrips to/from client      2   
parse count (total)                    2   
enqueue releases                       2   
session logical reads                  1   
execute count                          1   
db block gets                          1   
db block gets from cache               1   
session cursor cache hits              1   
user commits                           1   
redo entries                           1   
messages sent                          1   
redo synch writes                      1   
recursive calls                        1   
calls to kcmgas                        1   
db block changes                       1

cleanout항목이 100번가량 처리되고 나머지 400블록은 포기한 것으로 추측됨, 이것을 오락클 런-타임 전략인 "통계적 추측" 유형이라고 함

checkpoint 후 commit 시 Dirty Buffer활동 확인
update후checkpoint후commit후
{code}
OBJD COUNT(*)


--

--
267 2
58 2
18 2
68370 2
28 2
67130 2
424 3
75180 4
67127 4
4294967295 24
75179 546
{code}
{code}
OBJD COUNT(*)


--

--
67251 1
5874 1
5873 1
67544 1
5841 1
67714 1
289 1
68370 1
67740 1
68371 1
5839 1
5870 1
67738 1
5834 1
5875 1
383 1
287 1
18 1
4294967295 14
{code}
{code}
OBJD COUNT(*)


--

--
67251 1
5874 1
5873 1
459 1
457 1
67544 1
18 1
287 1
383 1
5841 1
67714 1
289 1
68370 1
67740 1
68371 1
456 6
4294967295 24
75179 500
{code}
  • update 후에, 504개 데이터블록과 212개의 언두 블록이 dirty블록상태이다.
  • checkpoint 후에는 메모리 내에 Dirty 버퍼가 존재하지 않는다.(checkpoint에 의해 버퍼를 복사됨/버퍼내용이 훼손된 것은 아니다.)
  • commit cleanout후, 500개의 테이블 블록과 2개의 언두블록이 dirty상태가 된다.
    => 결국 update와 commit 사이에 불필요한 작업으로 비효율이 발생 할 수 있다는 점이다.

Delayed block cleanout

세션이 데이터 블록을 읽을 때 uncommit트랜잭션으로 추정되는 정보(ITL 및 Row lock)를 확인하게 되면 읽기 일관성 버전을 생성하여 "정확한 언두 세그먼트 및 테이블 슬롯으로 무슨일이 발생했었는지" 확인하게 된다.

  • 해당 블록의 읽기 일관성 버전을 생성
  • 트랜젝션 상태확인을 위해 언두 세그먼트 헤더를 읽는다.
  • commit된 트랜잭션으로 확인되다면, ITL엔트리에 Commit SCN을 기록하고 Commit flag를 설정하고, Lock byte를 해제한다.

언두 세그먼트가 존재하지 않을 경우 :

Sys.Undo$에서 관리하고 있는 최종 Scn Base#과 Wrap#을 이용하여 Commit SCN이 변경경되어 블록 클린아웃이 진행된다.

언두 세그먼트 슬롯이 재사용된 경우 :

트랜젝션 컨트롤 내의 Scn은 방금 재사용된 트랜잭션 테이블 슬롯의 commit SCN으로 변경된다. 즉 원하는 슬롯의 wrap#가 transaction id의 wrap#보다 크다면, 트랜잭션 컨트롤 내의 scn을 획득한다.
1. 새로운 트랜잭션이 시작될 때마다,
2. 트랜잭션 테이블의 기존 정보를 새로운 트랜잭션의 첫 번째 언두 레코드에 저장하고,
3. 트랜잭션 컨트롤 내의 포인터는 해당 레코드를 가리키며,
4. 해당 언두 레코드는 기존 포인터 값을 저장한다.

트랜잭션 컨트롤(TRN CTL::)


TRN CTL: : seq: OxOb02 chd: Ox0011 ctl: Ox001c inc : Oxoooooooo nfb: Ox0001

TRN TBL:                                                                   
	index  state  cflags  wrap#    uel        scn      dba                                        
  --------------------------------------------------------------
...
  Ox11     9    OxOO    Ox2d25 Ox001b OxOOOO.041c818a Ox00805805                       
...
  Ox19     9    OxOO    Ox2d24 Ox002e OxOOOO.041c8ld1 Ox00805cOb                         
...
  Oxlb     9    OxOO    Ox2d23 Ox0019 OxOOOO.041c81ce Ox00805c09
  Oxlc     9    OxOO    Ox2d24 Oxffff OxOOOO.041c907c Ox00805cOd  
...
  Ox27     9    OxOO    Ox2d25 OxOOlc OxOOOO.041c9072 Ox00805cOd
...
  Ox2e     9    OxOO    Ox2dlf OxOOla OxOOOO.041c81d2 Ox00805806


-- 트랜잭션 컨트롤 영역에서 리스트의 헤드(chd)는 0x0011이고 꼬리(ctl)는 0x001c
-- 리스트의 헤드인 0x11(index 칼럼 값)의 uel칼럼 값은 0x001b를 가리키고
– 0x1b는 0x0019 ,0x19는 0x002e, 0x2e는 0x001a ... 0x27은 0x001c(리스트의꼬리인 ctl)
– 0x1c는 0xffff를 가리킴으로써 링크드 리스트는 끝내게 된다.

  • 현재 항목은 다음 번으로 사용할 항목을 가리키고 있으므로, 다음번 항목을 찾기 쉽다.(FIFO)
    freelist space management를 위해서는 LIFO링크드 리스트를 사용하기도 한다.
  • 대량의 index range스캔을 수행한다면, 인덱스 브랜치 블록을 경유하지 않고 현재 리프블록에서 다음블록으로 편리하게이동하도록 해주는
    forward 포인터가 사용된다.
    오라클은 desending index range 스캔을 허용하므로, 이를 위해 backward 포인터도 사용된다.|

검증방향 :
chd항목과 ctl항목, uel항목의 관계확인
언두레코드의 첫번째 레코드와 위 항목간의 관계확인


-- 트래이스로 변경사항 추적
    아래와 같은 시나리오로 진행
----------------------------------------
-- other session 17000 * update & commit
----------------------------------------
col owner format a10
col object_name format a10

select OWNER, OBJECT_NAME, OBJECT_ID, OBJECT_TYPE
  from dba_objects 
 where object_name in ('T1', 'T_WOONG');
  
set serveroutput on

alter system switch logfile;
alter system switch logfile;

alter system dump undo header '_SYSSMU10_3176102001$';
alter system dump undo header '_SYSSMU1_1518548437$';
alter system dump undo header '_SYSSMU2_2082490410$';
alter system dump undo header '_SYSSMU3_991555123$';
alter system dump undo header '_SYSSMU4_2369290268$';
alter system dump undo header '_SYSSMU5_1018230376$';
alter system dump undo header '_SYSSMU6_1834113595$';
alter system dump undo header '_SYSSMU7_137577888$';
alter system dump undo header '_SYSSMU8_1557854099$';
alter system dump undo header '_SYSSMU9_1126410412$';

alter system dump datafile 6 block 171;

update t_woong
   set v1 = 'AAAAA'
 where id = 1;

select xidusn, xidslot, xidsqn
  from v$transaction
 where addr=(select taddr from v$session where sid= SYS_CONTEXT('USERENV', 'SID'));
 
alter system dump datafile 6 block 171;
 
execute dump_undo_block

execute dump_log

그러나 납득할 만한 결과를 얻지 못함

트랜잭션 테이블의 읽기 일관성

트랜잭션 테이블의 읽기 일관성 버전을 생성하는 단계

1. 메모리 내에 언두 세그먼트 헤더블록을 복제한다.
2. 트랜잭션 컨트롤을 마지막으로 변경한 트랜잭션의 첫 번째 언두 레코드를 찾기 위해서 트랜잭션 컨트롤 내의 uba를 사용한다.
3. 해당 언두 레코드는 언두를 적용해야 할 트랜잭션 테이블 슬롯과 해당 슬롯에 대한 commit SCN을 알려주므로, 해당 언두를 복제본에 적용한다.
4. 이 시점에 트랜잭션 테이블과 트랜잭션 컨트롤은 과거 시점으로 한 걸음 이동한 것
5. 충분히 작은 scn을 발견하지 못했다면 현재보다 이전 버전의 트랜잭션 테이블을 생성하며, 언두레코드를 모두 사용한 후에도 적절한 값을 찾지 못했다면 ora-01555에러가 발생한다.

ORA-01555

"Snapshot too old"로 알려진 에러번호이다. 이것은 이력을 무한대로 유지할 수 없기 때문에 발생한다.
특정 언두 세그먼트의 크기가 매우 커짐으로써 발생하는 언두 테이블스페이스의 비효율성 문제를 automatic undo management를 이용하여 자동적인 offline, online, shrink등을 자동으로 관리하게 하여 효율성을 높였다.
그리고 자동 언두관리에서 undo-retention파라미터를 이용하여 이력을 유지할 시간을 지정하여 이력을 유지할 시간을 지정할 수 있게 되었다.