# 요약
  ■ Transaction 처리 순서
■ Commit
■ Latch
■ Lock
■ Redo 기능
■ LGWR에 의한 Redo 기록
■ Log Force at Commit
■ Write Ahead Log






■ Transaction 처리 순서

1. undo segment 할당, enq : US - contention
2. undo segment header에 transaction table slot 생성(못잡으면 대기)
    - transaction 정보를 저장하는 곳, TXID(Transaction ID)를 생성한다.
3. Transaction의 대상이 되는 블록을 데이터 버퍼 캐시에 실행계획을 통해서 찾는다.
    block header에 ITL(Interested Transaction List)에 transaction entry(slot) 등록
    만약에 ITL에 entry 등록할 공간이 없다면 공간이 확보할 때까지 대기해야한다.
    이때 발생한 대기 이벤트는 enq : TX - allocate ITL entry
4. 변경할 블록들의 변경 정보를 PGA 영역에서 change vector(이력정보)를 생성한다.
    - undo segment header 정보(change vector #1)
    - undo block (change vector #2)
    - Transaction의 대상이 되는 블록 (change vector #3)
    PGA 영역안에 chane vector 정보를 redo entry 라는 이름으로 redo log buffer로 복사한다.
    wait event : reoo copy latch, redo allocation latch, redo writing latch를 획득해야 한다.
    이 과정에서 latch 경합이 발생하면
        latch : redo copy
        latch : redo allocation
        latch : redo writing
        log buffer space wait event
        log file switch completion wait event : 현재 리두로그 알이이 꽉차서 로그 스위치가 발생하는 경우
                                                LGWR은 이 작업이 끝날 때까지 대기애해야한다.
5. 이전 이미지(값)에 대한 정보를 undo block에 기록하고 데이터 블록을 변경한다.
    변경된 block은 dirty 상태가 된다.
    변경된 block header에 CR(Consistent Read) 블록으로 작성해놓는다.
    변경하는 행에 대해서 lock을 생성한다.
    다른 세션에서 이행에 대해서 lock을 걸고 있으면 기다려야 한다.
    이때 발생한 대기 이벤트는 enq : TX - row lock contentions


select * from dba_tables where table_name = 'EMPLOYEES';
INI_TRANS MAX_TRANS
---------- ----------
         1        255

- ini trans : 데이터 블록에 동시에 접근 가능한 트랜잭션의 초기 수 (기본값 1, 23byte)
                트랜잭션이 많지 않은 경우 낭비를 하게 되므로 굳이 크게 설정할 필요는 없다.
- max trans : 최대 수 (10g, 255 고정) 
- pct free






■ Commit
- SCN(System Commit Number) 할당, commit 정보를 redo log buffer에 저장
- undo segment header의 transaction table에 commit이 완료되었다는 정보를 저장
- transaction entry, lock 해제
- LGWR 작동된다. redo log buffer의 내용을 redo log file에 기록






■ Latch
- 가벼운 lock(light-weight lock)
- 공유 메모리 영역(SGA)을 보호하기 위한 동기화 객체






■ Lock
- latch 보다는 무거운 동기화 객체
- 데이터베이스와 관련된 객체(object)를 보호하는 동기화 객체
- enqueue lock

   
  HR1>
drop table hr.emp purge;

create table hr.emp as select * from hr.employees;

update hr.emp set salary = salary * 1.1 where employee_id = 100;



HR2>
drop table hr.emp purge;

ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired

>> 테이블에 대해서는 TM lock, 트랜잭션 대상 행에 대해서는 TX lock


update hr.emp set salary = 3000 where employee_id = 100;

wait...



SYS>
select SID from v$session where username = 'HR';
    SID
-------
     27     HR1
    179     HR2


select * from v$lock where sid in (27,179);
ADDR     KADDR        SID TYPE        ID1        ID2      LMODE    REQUEST      CTIME      BLOCK
-------- -------- ------- ---- ---------- ---------- ---------- ---------- ---------- ----------
4956B4C8 4956B4F4      27 AE          100          0          4          0       6174          0
4956BB14 4956BB40     179 AE          100          0          4          0        124          0
4956BC1C 4956BC48      27 TO        79605          1          3          0        911          0
4956BE88 4956BEB4     179 TX       131091       3018          0          6 v        75          0
F6FAF810 F6FAF840      27 TM        88698          0          3          0        793          0
F6FAF810 F6FAF840     179 TM        88698          0          3          0         75          0
48F61060 48F610A0      27 TX       131091       3018          6 v         0        793          1 v

TYPE : TM
ID1  : object ID 88698

select owner,object_name,object_id,data_object_id,object_type,status
from dba_objects where object_id = 88698;
OWNER      OBJECT_NAME           OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE         STATUS
---------- -------------------- ---------- -------------- ------------------- -------------
HR         EMP                       88698          88698 TABLE               VALID

>  TM객체 정보 확인


TYPE : TX
ID1  : undo segment 번호 + transaction slot 번호
ID2  : transaction slot sequence

LMODE > 0 이면 Lock을 보유중인 세션
REQUEST > 0 이면 Lock을 요청중인 세션
CTIME : 현재 lock 모드가 허용된 이후의 시간(second), 즉 lock을 보유하거나 요청한 이후부터의 시간(초)
BLOCK :  현재 lock이 다른 lock을 블로킹하고 있는지 여부
        1 : 다른 lock을 블로킹 중이다.
        0 : 블로킹하지 않음.


select sid,blocking_session,event,prev_sql_id from v$session where username = 'HR';
    SID BLOCKING_SESSION EVENT                                         PREV_SQL_ID
------- ---------------- --------------------------------------------- -------------
     27                  SQL*Net message from client                   43qd0u7n4j2nk
    179               27 enq: TX - row lock contention                 dyk4dprp70d74


select sql_text from v$sql where sql_id = '43qd0u7n4j2nk';
SQL_TEXT
--------------------------------------------------
update hr.emp set salary = salary * 1.1 where employee_id = 100



HR1>
rollback;

Rollback complete.


HR2>
1 row updated.


select sid,blocking_session,event,prev_sql_id from v$session where username = 'HR';
    SID BLOCKING_SESSION EVENT                                         PREV_SQL_ID
------- ---------------- --------------------------------------------- -------------
     27                  SQL*Net message from client                   43qd0u7n4j2nk
    179                  SQL*Net message from client                   bupask1tubt3r


LMODE
- 2 : Row Share(RS), Sub Share(SS)
    lock 된 테이블에 대한 동시 엑세스를 허용하지만 세션이 배타적(exclusive) 액세스를 위해 전체 테이블 lock하는 것은 금지한다.
        LOCK TABLE hr.emp IN ROW SHARE MODE;
    
- 3 : Row eXclusive(RX), Sub eXclusive(SX)
    Row Share 와 동일하지만 Shared mode에서도 lock을 금지한다.
    Row eXclusive Lock은 데이터 갱신, 입력, 삭제 시 자동으로 획득한다.
        LOCK TABLE hr.emp IN ROW EXCLUSIVE MODE;

- 4 : Share(S)
    동시 query는 허용하지만 lock된 테이블에 대해 변경은 금지한다.
    테이블에서 인덱스 생성 시 share lock이 필요하며 자동으로 요청한다.
    unique, primary key 충돌 시 발생
        LOCK TABLE hr.emp IN ROW SHARE MODE;

- 5 : Share Row eXclusive(SRX), Share Sub eXclusive(SSX)
    전체 테이블을 query하는데 사용되며 다른 유저가 테이블 행을 query하는 것은 허용하지만 해당 테이블을 share mode에서
    lock하거나 행을 갱신하는 것은 금지한다. 일반테이블보단 딕셔너리 테이블
    row cache lock(sequence nocache 속성에서 nextval 호출할 때마다 dictionary 변경)
        LOCK TABLE hr.emp IN SHARE ROW EXCLUSIVE MODE;

- 6 : eXclusive(X)
    lock된 테이블에서 query는 허용하지만 해당 테이블에서의 다른 작업은 금지 한다.
    즉, 테이블에 DDL 작업 시에 exclusive lock이 필요하다.
        LOCK TABLE hr.emp IN EXCLUSIVE MODE;





■ Redo 기능
- Database 복구를 목적으로 설계
- Database에 적용된 모든 변경 사항에 대한 이력 저장
- DML/DDL/Recursive SQL(select ~ from ~ for update)에 의해 변경된 모든 Data 이력 (nologging 제외)
- DDL Text 저장 (DML Text 제외)



■ LGWR에 의한 Redo 기록
- Redo Buffer 내용을 Redo Log File 에 기록하는 시점
- 매 3초 마다
- Log Buffer의 1/3 또는 1MB 가 저장될 때
- User Process가 Commit 또는 Rollback으로 Transaction을 종료할 때 (Log Force at Commit)
- DBWR Process에 의해 신호를 받을 때 (write ahead logging)



■ Log Force at Commit
- Transaction과 관련된 모든 Redo Record를 Log File에 저장 후 Commit 완료



■ Write Ahead Log
- Data Buffer에 기록하기 전에 Log Buffer에 먼저 기록
- Data File에 기록하기 전에 Log File에 먼저 기록

8i : 버전 까지는 redo allocation latch 시스템에 1개여서 오직 하나의 프로세스만이 redo buffer에서 메모리 할당할 수 있었다.

9i  : shared redo strands : redo buffer의 영역을 일정 개수로 분할해서 사용. 분할된 영역을 strand라고 한다.
   _log_parallelism 파라미터의 값을 이용해서 redo strand의 개수를 지정.(cpu수/8) 권장사항
   각 strand 마다 redo allocation latch 사용

10g  :  - shared redo strands 개수를 오라클이 동적으로 관리한다. _log_parallelism_dynamic 히든 파라미터를 TRUE로 설정.
- private redo strands 기능을 사용함으로써 리두데이터를 pga 영역에서 change vector를 생성하는것이 아닌 
   shared pool에 private strands 영역에 저장하며 이 영역에 저장된 로그 데이터는 redo buffer를 거치지 않고 redo log file에 저장함으로써 
   latch 경합을 최소화 한다. _log_private_parallelism 히든 파라미터의 값이 true로 설정하면 private redo strands 기능이 활성화된다.

10gR2 private redo strands를 위한 공간은 LOG_BUFFER내에 생성 되며 _log_pivate_mul 에 지정된 비율만큼(5퍼센트)을 private redo strands공간으로 사용,
        'zero copy redo' 라고도 불린다.


SELECT a.ksppinm  Parameter, b.ksppstvl  Value
FROM x$ksppi a,   x$ksppcv b
WHERE a.indx = b.indx AND a.ksppinm in ('_log_parallelism_dynamic','_log_private_mul');
PARAMETER                                          VALUE
-------------------------------------------------- ----------
_log_parallelism_dynamic                           TRUE
_log_private_mul                       


redo log buffer latch
- latch : redo copy
- latch : redo allocation 
- latch : redo writing

# Redo Log : Insert
  SYS>

# 리두 모니터링 통계 정보
select n.name, sum(s.value)
from v$sesstat s, v$statname n
where n.name in ('redo entries','redo size', 'redo synch writes','redo writes','redo blocks written',
                'redo log space requests','redo log space wait time')
and s.statistic# = n.statistic#
and s.sid = (select sid from v$mystat where rownum = 1)
group by n.name;
NAME                           SUM(S.VALUE)
------------------------------ ------------
redo entries                              4  redo entry가 redo log buffer에 기록된 횟수
redo size                               892  redo size (byte)
redo log space requests                   0  redo log buffer에 redo entry들을 LGWR가 redo log file에 
                                                쓰려고 하는데 log file이 꽉 차서 log switch 발생한 횟수
redo log space wait time                  0  redo log space requests에 소요된 시간(1/100초)
redo synch writes                         1  commit, rollback에 의해 수행된 redo write 수
redo blocks written                       0  redo log file에 write된 redo log block 수
redo writes                               0  LGWR 수행한 수

drop table hr.redo_table purge;

create table hr.redo_table(id number, name char(100));
> create > 딕셔너리 조작, 갱신

insert into hr.redo_table(id,name) select object_id, object_name from dba_objects;

select t.used_ublk, t.used_urec
from v$transaction t, v$session s
where s.sid = (select sid from v$mystat where rownum = 1)
and s.taddr = t.addr; 
 USED_UBLK  USED_UREC
---------- ----------
        43       2487


select n.name, sum(s.value)
from v$sesstat s, v$statname n
where n.name in ('redo entries','redo size', 'redo synch writes','redo writes','redo blocks written',
                'redo log space requests','redo log space wait time')
and s.statistic# = n.statistic#
and s.sid = (select sid from v$mystat where rownum = 1)
group by n.name;
NAME                           SUM(S.VALUE)
------------------------------ ------------
redo entries                           7816
redo size                          10964516
redo log space requests                   0
redo log space wait time                  0
redo synch writes                         4
redo blocks written                       0
redo writes                               0


select blocks, bytes/1024/1024 mb from dba_segments where segment_name = 'REDO_TABLE';
    BLOCKS         MB
---------- ----------
      1408         11

>> 사용한 블럭과 용량은 그대로 있다.



SYS@ora11g> rollback;



# Redo Log : Logging Mode + Append
  SYS2> new session

select n.name, sum(s.value)
from v$sesstat s, v$statname n
where n.name in ('redo entries','redo size', 'redo synch writes','redo writes','redo blocks written',
                'redo log space requests','redo log space wait time')
and s.statistic# = n.statistic#
and s.sid = (select sid from v$mystat where rownum = 1)
group by n.name;
NAME                                          SUM(S.VALUE)
--------------------------------------------- ------------
redo entries                                             0
redo size                                                0
redo log space requests                                  0
redo log space wait time                                 0
redo synch writes                                        0
redo blocks written                                      0
redo writes                                              0


insert /*+ append */ into hr.redo_table(id,name) select object_id, object_name from dba_objects;



select t.used_ublk, t.used_urec
from v$transaction t, v$session s
where s.sid = (select sid from v$mystat where rownum = 1)
and s.taddr = t.addr; 
 USED_UBLK  USED_UREC
---------- ----------
         2          2


select n.name, sum(s.value)
from v$sesstat s, v$statname n
where n.name in ('redo entries','redo size', 'redo synch writes','redo writes','redo blocks written',
                'redo log space requests','redo log space wait time')
and s.statistic# = n.statistic#
and s.sid = (select sid from v$mystat where rownum = 1)
group by n.name;
NAME                                          SUM(S.VALUE)
--------------------------------------------- ------------
redo entries                                          1535
redo size                                         11027116
redo log space requests                                  0
redo log space wait time                                 0
redo synch writes                                        1
redo blocks written                                      0
redo writes                                              0


select blocks, bytes/1024/1024 mb from dba_segments where segment_name = 'REDO_TABLE';
    BLOCKS         MB
---------- ----------
      2816         22




rollback;


truncate table hr.redo_table;


select blocks, bytes/1024/1024 mb from dba_segments where segment_name = 'REDO_TABLE';
    BLOCKS         MB
---------- ----------
         8      .0625

> truncate 사용 시 min extent 1개 남김



> /*+ append */ 
> HWM 뒤에 있는 블럭을 사용하여 추가

> 앞에 빈공간 무시하고 새로운 extent 를 맨 뒤에 추가하여 사용
사용으로 언두, 리두 사용량 줄어듬 -> 복구 목적이 없기 때문에 사용량이 적다 

> 기본 

원본데이터가 따로 있어 
, 블럭 사용량은 늘어남

블럭사용량 처음 insert + insert



# Redo Log : No Logging Mode + Append
  new session>

select n.name, sum(s.value)
from v$sesstat s, v$statname n
where n.name in ('redo entries','redo size', 'redo synch writes','redo writes','redo blocks written',
                'redo log space requests','redo log space wait time')
and s.statistic# = n.statistic#
and s.sid = (select sid from v$mystat where rownum = 1)
group by n.name;
NAME                                          SUM(S.VALUE)
--------------------------------------------- ------------
redo entries                                             0
redo size                                                0
redo log space requests                                  0
redo log space wait time                                 0
redo synch writes                                        0
redo blocks written                                      0
redo writes                                              0


select logging from dba_tables where table_name = 'REDO_TABLE';
LOGGING
-------
YES


alter table hr.redo_table nologging;


select logging from dba_tables where table_name = 'REDO_TABLE';
LOGGING
-------
NO


select n.name, sum(s.value)
from v$sesstat s, v$statname n
where n.name in ('redo entries','redo size', 'redo synch writes','redo writes','redo blocks written',
                'redo log space requests','redo log space wait time')
and s.statistic# = n.statistic#
and s.sid = (select sid from v$mystat where rownum = 1)
group by n.name;
NAME                                          SUM(S.VALUE)
--------------------------------------------- ------------
redo entries                                             6
redo size                                             2296
redo log space requests                                  0
redo log space wait time                                 0
redo synch writes                                        1
redo blocks written                                      0
redo writes                                              0

>> 딕셔너리에 대한 변경 작업
ddl 문장 수행

  new session>

select n.name, sum(s.value)
from v$sesstat s, v$statname n
where n.name in ('redo entries','redo size', 'redo synch writes','redo writes','redo blocks written',
                'redo log space requests','redo log space wait time')
and s.statistic# = n.statistic#
and s.sid = (select sid from v$mystat where rownum = 1)
group by n.name;
NAME                                          SUM(S.VALUE)
--------------------------------------------- ------------
redo entries                                             0
redo size                                                0
redo log space requests                                  0
redo log space wait time                                 0
redo synch writes                                        0
redo blocks written                                      0
redo writes                                              0


insert /*+ append */ into hr.redo_table(id,name) select object_id, object_name from dba_objects;


select t.used_ublk, t.used_urec
from v$transaction t, v$session s
where s.sid = (select sid from v$mystat where rownum = 1)
and s.taddr = t.addr; 
 USED_UBLK  USED_UREC
---------- ----------
         1          1


select n.name, sum(s.value)
from v$sesstat s, v$statname n
where n.name in ('redo entries','redo size', 'redo synch writes','redo writes','redo blocks written',
                'redo log space requests','redo log space wait time')
and s.statistic# = n.statistic#
and s.sid = (select sid from v$mystat where rownum = 1)
group by n.name;
NAME                                          SUM(S.VALUE)
--------------------------------------------- ------------
redo entries                                           419
redo size                                            50200
redo log space requests                                  0
redo log space wait time                                 0
redo synch writes                                        1
redo blocks written                                      0
redo writes                                              0


select blocks, bytes/1024/1024 mb from dba_segments where segment_name = 'REDO_TABLE';
    BLOCKS         MB
---------- ----------
      1408         11


SYS@ora11g> rollback;


select logging from dba_tables where table_name = 'REDO_TABLE';
LOGGING
-------
NO


alter table hr.redo_table logging;


select logging from dba_tables where table_name = 'REDO_TABLE';
LOGGING
-------
YES


> 테이블에 대한 리두가 필요없다면 nologging mode 로 성능 향상
> 대량 데이터를 부어넣을 때 사용
> logging mode로 복구