[아이티윌 오라클 DBA 과정 91기] 260130 TIL

2026. 1. 30. 18:52Courses/아이티윌 오라클 DBA 과정

Redo Log Buffer

  • 데이터베이스 데이터 블록의 모든 변경 사항을 기록
  • 기본 목적은 recovery(복구)
  • 내부에 기록된 변경 사항을 redo entry 라고 함
  • redo entry 에는 변경 사항을 재구성하거나 재실행 할 정보가 포함이 되어 있음
  • DML/DDL(create, alter, drop)/select for update
  • DDL(create, alter, drop) text로 저장
  • log_buffer 파라미터로 정의

LGWR 작동

  • redo log buffer에 있는 redo entry를 redo log file에 기록
  • commit, rollback
  • redo log buffer 1/3 사용한 경우
  • 1MB 이상 리두 정보가 입력될 경우
  • 3초마다
  • DBWR가 database buffer cache에 있는 dirty buffer를 datafile에 기록하기 전에 LGWR가 먼저 작동

Redo Entry 생성 과정

  • 버퍼 캐시의 블록을 변경하기 전에 redo log buffer에 redo entry를 먼저 생성

  1. 변경 정보를 PGA 영역에 change vector 생성
    • undo segment header 정보(change vector #1)
    • undo block (change vector #2)
    • transaction 대상이 되는 블록(change vector #3)
  2. PGA 영역 안에 있는 change vector 를 redo entry라는 이름으로 redo log buffer로 복사
    • 이 때 반드시 redo copy latch를 획득해야 하며 change vector를 redo log buffer에 복사하는 전 과정동안 redo copy latch를 보유해야 함
      • 이 과정에서 경합이 발생하면 latch: redo copy 대기 이벤트가 발생
  3. redo allocation latch를 획득한 후 리두 로그 버퍼 내에 redo entry를 저장할 공간을 확보
    • 이 과정에서 경합이 발생하면 latch: redo allocation 대기 이벤트 발생
    • 만약에 리두 로그 버퍼에 free 공간이 부족하면 redo writing latch 를 획득한 후 LGWR에게 리두 로그 버퍼에 있는 리두 엔트리를 리두 로그 파일에 기록하도록 요청하고 redo writing latch 는 해제
      • 이 과정에서 경합이 발생하면 latch: redo writing 대기 이벤트 발생
    • 리두 로그 버퍼에 free 공간이 확보될 때까지 log buffer space 대기 이벤트가 발생
    • LGWR가 리두 엔트리를 current redo log file에 기록하려는 시점에 현재 그룹이 꽉 차서 log switch가 발생하는 경우 LGWR는 기다려야 함
      • 이 때 log file switch completion 대기 이벤트 발생
    • 리두 로그 버퍼에 필요한 공간이 확보가 되면 redo allocation latch는 해제
  4. change vector를 리두 로그 버퍼에 redo entry 형태로 복사
    • 이 과정이 끝나면 redo copy latch를 해제

Redo Copy Latch

  • PGA 영역에 생성한 Change vector를 리두 로그 버퍼로 복사하려는 경우 전체 과정동안 redo copy latch를 획득해야 함
  • CPU 개수의 2배(기본값)
  • latch: redo copy
SYS@ora19c> col parameter format a30
SYS@ora19c> select a.ksppinm parameter, b.ksppstvl value
from x$ksppi a, x$ksppcv b
where a.indx = b.indx
and a.ksppinm = '_log_simultaneous_copies';  2    3    4

PARAMETER                      VALUE
------------------------------ ------------------------------
_log_simultaneous_copies       4

SYS@ora19c> select name, gets from v$latch_children where name = 'redo copy';

NAME                                                     GETS
-------------------------------------------------- ----------
redo copy                                                 211
redo copy                                                 213
redo copy                                                 212
redo copy                                                 211

Redo Allocation Latch

  • change vector를 리두 로그 버퍼에 복사하기 위해 리두 로그 버퍼 내에 프리 공간을 확보해야 함
  • 이 과정에서 redo allocation latch를 획득해야 함
  • latch: redo allocation
  • 8i : 하나의 redo allocation latch만 사용
  • 9i : redo log buffer를 복수개의 redo strand 라는 공간으로 분할해서 사용
    • strand 마다 redo allocation latch를 사용하도록 함
    • log_parallelism 파라미터로 결정 기본 값 1
  • 10gR1 : redo strand 개수를 오라클이 동적으로 관리
    • _log_parallelism_dynamic 히든 파라미터 값이 true(기본 값)로 설정되어있음
SYS@ora19c> 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               5 <<- shared pool의 5%를 private redo strand로 사용

SYS@ora19c> select count(*) from v$latch_children where name = 'redo allocation';

  COUNT(*)
----------
        29
  • 10gR2 : private redo strand 기능 사용함으로써 redo 데이터를 PGA 영역에서 change vector를 생성하는 것이 아니고 shared pool에 private redo strand 영역에 저장하며 이 영역에 저장된 redo 데이터는 redo log buffer를 거치지 않고 redo log file에 바로 저장
    • redo copy latch, redo allocation latch를 획득하지 않아도 됨(zero copy redo)
    • private redo strand 는 shared pool 공간에 _log_private_mul 히든 파라미터로 지정된 비율(5%)만큼을 사용
SYS@ora19c> select * from v$sgastat where name = 'private strands';

POOL           NAME                                BYTES     CON_ID
-------------- ------------------------------ ---------- ----------
shared pool    private strands                   3677184          0

Redo Writing Latch

  • redo log buffer 내의 공간을 확보하기 위해 LGWR에게 쓰기 요청을 하려는 경우 redo writing latch를 획득해야 함
  • 1개
  • latch : redo writing
SYS@ora19c> select name, gets from v$latch_parent where name = 'redo writing';

NAME                                 GETS
------------------------------ ----------
redo writing                       942524

Conventional Path Load

  • 데이터 버퍼 캐시를 사용해서 데이터 파일에 쓰기 작업 수행

테이블 생성

create table hr.redo_table(id number, name char(200)) tablespace users;

특정 유저 세션에 대한 리두 통계 정보 조회

SYS@ora19c> 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$session where username = 'HR')
group by n.name;  2    3    4    5    6

no rows selected

# HR로 접속 시 정보가 나옴
SYS@ora19c> 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$session where username = 'HR')
group by n.name;  2    3    4    5    6

NAME                           SUM(S.VALUE)
------------------------------ ------------
redo log space wait time                  0
redo synch writes                         1
redo writes                               0
redo blocks written                       0
redo entries                              2
redo size                               676
redo log space requests                   0

7 rows selected.
  • redo log space wait time : redo log space request에 소요된 시간(1/1000초)
  • redo synch writes : commit, rollback에 의해 수행된 redo write 수
  • redo writes : LGWR 수행한 수
  • redo blocks written : redo log file에 write된 redo log block 수
  • redo entries : redo entry가 redo log buffer에 기록된 수
  • redo size : redo size(byte)
  • redo log space requests : redo log buffer에 redo entry를 LGWR가 redo log file에 쓰려고 하는데 log file이 꽉 차서 log switch 발생한 수

HR : insert 수행

HR@ora19c> insert into hr.redo_table(id, name) select object_id, object_name from all_objects;

54956 rows created.
SYS@ora19c> 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$session where username = 'HR')
group by n.name;   2    3    4    5    6

NAME                           SUM(S.VALUE)
------------------------------ ------------
redo log space wait time                  0
redo synch writes                         2
redo writes                               0
redo blocks written                       0
redo entries                           9261
redo size                          13175568
redo log space requests                   0

7 rows selected.
  • redo entry와 size가 증가함
  • redo synch writes도 1 증가함 → 내부적으로 commit/rollback 발생
    • commit/rollback이 발생했으므로 lgwr가 동작하지만 redo writes는 증가하지 않음
    • redo synch writes 수로 따로 관리
    • LGWR 작동 수는 = redo synch writes + redo wrties
SYS@ora19c> select s.sid, s.username, t.xidusn, xidslot, xidsqn, t.ubafil, t.ubablk, t.used_ublk
from v$session s, v$transaction t
where s.saddr = t.ses_addr
and s.username = 'HR';  2    3    4

       SID USERNAME                           XIDUSN    XIDSLOT     XIDSQN     UBAFIL     UBABLK  USED_UBLK
---------- ------------------------------ ---------- ---------- ---------- ---------- ---------- ----------
       168 HR                                      5         15       2641         4        1784         39

HR : rollback

SYS@ora19c> 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$session where username = 'HR')
group by n.name;  2    3    4    5    6

NAME                           SUM(S.VALUE)
------------------------------ ------------
redo log space wait time                  0
redo synch writes                         3
redo writes                               0
redo blocks written                       0
redo entries                          15025
redo size                          14021144
redo log space requests                   0

7 rows selected.
  • redo sync writes 수만 1 증가

HR : truncate

HR@ora19c> truncate table hr.redo_table;

Table truncated.
SYS@ora19c> 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$session where username = 'HR')
group by n.name;  2    3    4    5    6

NAME                           SUM(S.VALUE)
------------------------------ ------------
redo log space wait time                  0
redo synch writes                         4
redo writes                               0
redo blocks written                       0
redo entries                          15291
redo size                          14057948
redo log space requests                   0

7 rows selected.
  • truncate 시 내부적으로 auto commit이 발생하므로(딕셔너리 테이블에 변경 작업을 수행하기 때문) redo synch writes가 1 증가
  • redo entries와 redo size도 증가 → redo 가 생성됨

Direct Path Write

  • 데이터 버퍼 캐시를 사용하지 않고 데이터 파일에 직접 쓰기 작업을 수행 (서버 프로세스)
  • HWM 이후에 블록을 추가(append)
    • 데이터가 듬성듬성 있는 경우 테이블 재구성 후 direct path write 수행하는 것이 좋음
  • 추가되는 데이터에 대해서 undo 발생량이 최소화됨(딕셔너리에 대한 undo는 발생)
  • 테이블에 대해서 TM Lock Exclusive(6) 모드 Lock을 설정하기 때문에 다른 세션에서 DML 작업이 허용되지 않음

SYS@ora19c> 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$session where username = 'HR')
group by n.name;   2    3    4    5    6

NAME                           SUM(S.VALUE)
------------------------------ ------------
redo log space wait time                  0
redo synch writes                         1
redo writes                               0
redo blocks written                       0
redo entries                              2
redo size                               676
redo log space requests                   0

7 rows selected.

테이블에 DML 작업을 수행하면 REDO 정보 생성(yes 기본값)

SYS@ora19c> select logging from dba_tables where table_name = 'REDO_TABLE';

LOG
---
YES

HR : insert 수행

insert /*+ append */ into hr.redo_table(id, name) select object_id, object_name from all_objects;
  • append : direct path write를 유도하는 힌트

UNDO

SYS@ora19c> select s.sid, s.username, t.xidusn, xidslot, xidsqn, t.ubafil, t.ubablk, t.used_ublk
from v$session s, v$transaction t
where s.saddr = t.ses_addr
and s.username = 'HR';  2    3    4

       SID USERNAME                           XIDUSN    XIDSLOT     XIDSQN     UBAFIL     UBABLK  USED_UBLK
---------- ------------------------------ ---------- ---------- ---------- ---------- ---------- ----------
       168 HR                                      8          5       2607         0           0          1
  • undo 발생이 줄음

REDO

SYS@ora19c> 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$session where username = 'HR')
group by n.name;  2    3    4    5    6

NAME                           SUM(S.VALUE)
------------------------------ ------------
redo log space wait time                  0
redo synch writes                         2
redo writes                               0
redo blocks written                       0
redo entries                           2225
redo size                          13417996
redo log space requests                   1

7 rows selected.
  • redo 생성을 활성화 시킨 상태에서 작업했기 때문에 redo가 줄어들지 않음

Redo 발생 최소화

SYS@ora19c> 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$session where username = 'HR')
group by n.name;  2    3    4    5    6

NAME                           SUM(S.VALUE)
------------------------------ ------------
redo log space wait time                  0
redo synch writes                         1
redo writes                               0
redo blocks written                       0
redo entries                              2
redo size                               676
redo log space requests                   0

7 rows selected.

nologging 모드로 변경

SYS@ora19c> alter table hr.redo_table nologging;

Table altered.

SYS@ora19c> select logging from dba_tables where table_name = 'REDO_TABLE';

LOG
---
NO

HR : insert 수행

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

Redo

SYS@ora19c> 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$session where username = 'HR')
group by n.name;  2    3    4    5    6

NAME                           SUM(S.VALUE)
------------------------------ ------------
redo log space wait time                  0
redo synch writes                         1
redo writes                               0
redo blocks written                       0
redo entries                             31
redo size                              2864
redo log space requests                   0

7 rows selected.
  • redo 발생량이 크게 줄음

UNDO

SYS@ora19c> select s.sid, s.username, t.xidusn, xidslot, xidsqn, t.ubafil, t.ubablk, t.used_ublk
from v$session s, v$transaction t
where s.saddr = t.ses_addr
and s.username = 'HR';  2    3    4

       SID USERNAME                           XIDUSN    XIDSLOT     XIDSQN     UBAFIL     UBABLK  USED_UBLK
---------- ------------------------------ ---------- ---------- ---------- ---------- ---------- ----------
       168 HR                                      4         19       2130         0           0          1

rollback 수행 후 세그먼트 정보 조회

SYS@ora19c> select segment_name, bytes/1024/1024 mb, blocks, extents from dba_segments where segment_name = 'REDO_TABLE';

SEGMENT_NAME                           MB     BLOCKS    EXTENTS
------------------------------ ---------- ---------- ----------
REDO_TABLE                             13       1664         28
  • 데이터가 입력되지 않았지만 block 및 extents 수가 증가된 채로 있음

truncate 수행 후 세그먼트 조회

SYS@ora19c> select segment_name, bytes/1024/1024 mb, blocks, extents from dba_segments where segment_name = 'REDO_TABLE';

SEGMENT_NAME                           MB     BLOCKS    EXTENTS
------------------------------ ---------- ---------- ----------
REDO_TABLE                          .0625          8          1
  • minextent 1개만 남기고 공간이 모두 회수됨

redo 발생할 수 있도록 logging 모드로 다시 수정

SYS@ora19c> alter table hr.redo_table logging;

Table altered.

SYS@ora19c> select logging from dba_tables where table_name = 'REDO_TABLE';

LOG
---
YES

Commit/Rollback

SYS@ora19c> select n.name, sum(s.value)
from v$sesstat s, v$statname n
where n.name in ('redo synch writes', 'user commits', 'user rollbacks')
and s.statistic# = n.statistic#
and s.sid = (select sid from v$session where username = 'HR')
group by n.name;  2    3    4    5    6

NAME                           SUM(S.VALUE)
------------------------------ ------------
user rollbacks                            0
redo synch writes                         1
user commits                              0

HR : Insert 수행 후 commit

HR@ora19c> insert into hr.redo_table(id, name) values(1, 'oracle');

1 row created.

HR@ora19c> commit;

Commit complete.
SYS@ora19c> select n.name, sum(s.value)
from v$sesstat s, v$statname n
where n.name in ('redo synch writes', 'user commits', 'user rollbacks')
and s.statistic# = n.statistic#
and s.sid = (select sid from v$session where username = 'HR')
group by n.name;  2    3    4    5    6

NAME                           SUM(S.VALUE)
------------------------------ ------------
user rollbacks                            0
redo synch writes                         3
user commits                              1

SYS@ora19c> select event, total_waits, time_waited
from v$session_event
where sid = (select sid from v$session where username = 'HR')
and event = 'log file sync';  2    3    4

EVENT                          TOTAL_WAITS TIME_WAITED
------------------------------ ----------- -----------
log file sync                            3           2
  • user commits 수가 증가
  • redo synch writes 수도 증가

HR : Insert 수행 후 commit

HR@ora19c> insert into hr.redo_table(id, name) values(2, 'james');

1 row created.

HR@ora19c> commit;

Commit complete.
SYS@ora19c> select n.name, sum(s.value)
from v$sesstat s, v$statname n
where n.name in ('redo synch writes', 'user commits', 'user rollbacks')
and s.statistic# = n.statistic#
and s.sid = (select sid from v$session where username = 'HR')
group by n.name;  2    3    4    5    6

NAME                           SUM(S.VALUE)
------------------------------ ------------
user rollbacks                            0
redo synch writes                         4
user commits                              2

SYS@ora19c> select event, total_waits, time_waited
from v$session_event
where sid = (select sid from v$session where username = 'HR')
and event = 'log file sync';  2    3    4

EVENT                          TOTAL_WAITS TIME_WAITED
------------------------------ ----------- -----------
log file sync                            4           3
  • user commits 수가 증가
  • redo synch writes 수도 증가
  • log file sync 대기 이벤트 발생 수 증가

HR : Delete 수행 후 rollback

HR@ora19c> delete from hr.redo_table where id = 2;

1 row deleted.

HR@ora19c> rollback;

Rollback complete.
SYS@ora19c> select n.name, sum(s.value)
from v$sesstat s, v$statname n
where n.name in ('redo synch writes', 'user commits', 'user rollbacks')
and s.statistic# = n.statistic#
and s.sid = (select sid from v$session where username = 'HR')
group by n.name;  2    3    4    5    6

NAME                           SUM(S.VALUE)
------------------------------ ------------
user rollbacks                            1
redo synch writes                         6
user commits                              2

SYS@ora19c> select event, total_waits, time_waited
from v$session_event
where sid = (select sid from v$session where username = 'HR')
and event = 'log file sync';  2    3    4

EVENT                          TOTAL_WAITS TIME_WAITED
------------------------------ ----------- -----------
log file sync                            6           4
  • user rollbacks 수가 증가
  • redo synch writes 수도 증가
  • log file sync 대기 이벤트 발생 수 증가

정리

  • user commits/user rollbacks는 유저가 직접 commit/rollback 한 수만 보여줌
  • redo synch writes는 내부적으로 수행된 commit/rollback 횟수도 보여줌

log file sync 대기 이벤트

  • commit/rollback 수행 시 LGWR가 redo log buffer에 있는 redo entry 를 redo log file에 기록하는데, 이 때 redo synch writes, user commits, user rollbacks 통계 값이 증가
  • 서버 프로세스는 LGWR가 write가 끝날 때까지 대기
    • 이 때 발생하는 대기 이벤트가 log file sync
  • log file sync 대기 이벤트는 오라클에서 가장 보편적인 대기 이벤트 중 하나
    • synch write가 수행되는 시간은 매우 짧기 때문에 log file sync 대기는 크게 문제가 되지 않음
    • but 너무 자주 발생할 경우 문제

log file sync 대기 원인

  • 빈번한 commit, rollback 수행하는 경우
  • I/O 시스템이 느린 경우(LGWR는 log file parallel write 대기 이벤트가 평균 시간이 높거나 전체 시스템에서 대기 시간 대비 차지하는 비중이 높다면 리두 로그 파일이 있는 I/O 시스템 성능에 문제가 있음)
  • 리두 데이터가 불필요하게 생성되는 경우 (nologging 옵션 고려, direct path write 고려)
  • 시퀀스 생성 시에 nocache(row cache lock, log file sync)로 설정하면 sequence.nextval 수행하는 순간 딕셔너리(seq$)테이블 갱신하고 commit 수행하기 때문에 시퀀스를 사용할 때 트랜잭션 양을 고려하여 반드시 적절한 크기의 cache 속성을 부여하자
  • 리두 로그 버퍼가 너무 큰 경우