프린트 하기

OS 환경 : Oracle Linux 8.7 (64bit)

 

DB 환경 : Oracle Database 19.27.0.0

 

방법 : 오라클 19c 핫백업시 redo 발생량 차이 확인

본문에서는 오라클 19c에서 핫백업시 실행시점과 미실행시점에 dml시 각각 redo 로그가 얼마나 발생하는지 차이를 확인해봄

 

 

테스트
환경 확인
1. 핫백업(begin 백업) 미실행시 redo 발생량 측정
2. 핫백업(begin 백업) 실행시 redo 발생량 측정

 

 

테스트
환경 확인
아카이브로그 모드가 맞는지 확인

1
2
3
4
5
6
7
SQL> archive log list
Database log mode              Archive Mode
Automatic archival             Enabled
Archive destination            /oraimsi
Oldest online log sequence     149
Next log sequence to archive   151
Current log sequence           151

아카이브로그 모드임

 

 

현재 redo 크기 확인

1
2
3
4
5
6
7
8
9
10
11
12
13
SQL>
set lines 200 pages 1000
col member for a50
select l.group#, member, archived, l.status, (bytes/1024/1024) MB
from v$log l, v$logfile f
where f.group# = l.group#
order by 1;
 
    GROUP# MEMBER                                             ARC STATUS                   MB
---------- -------------------------------------------------- --- ---------------- ----------
         1 /app/oracle/oradata/ORA19DBFS/redo01.log           YES INACTIVE               1024
         2 /app/oracle/oradata/ORA19DBFS/redo02.log           NO  CURRENT                1024
         3 /app/oracle/oradata/ORA19DBFS/redo03.log           YES INACTIVE               1024

1gb임

 

 

1. 핫백업(begin 백업) 미실행시 redo 발생량 측정
샘플 테이블 생성

1
2
3
4
5
6
7
8
SQL>
drop table sample_t purge;
create table sample_t 
(id1 number, id2 number, name varchar2(10),  
date1 varchar2(8), date2 varchar2(8), date3 date, 
phone varchar2(13), price number, qty number, 
test1 number,  test2 varchar2(5), test3 varchar2(4)
);

 

 

데이터 삽입(100만건)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
SQL>
set timing on
DECLARE
TYPE tbl_ins IS TABLE OF sample_t%ROWTYPE INDEX BY BINARY_INTEGER;
w_ins tbl_ins;
BEGIN
FOR d IN 1..10 LOOP
FOR i IN 1..100000 LOOP 
   w_ins(i).id1   := (d - 1) * 100000 + i;
   w_ins(i).id2   := i||ceil(dbms_random.value(1, 10000000));
   w_ins(i).name  := dbms_random.string('x',5);
   w_ins(i).date1 := round(dbms_random.value(2010,2021))||to_char(round(dbms_random.value(1,12)), 'FM09')||to_char( round(dbms_random.value(1,28)), 'FM09');
   w_ins(i).date2 := '2021'||to_char(round(dbms_random.value(1,12)) , 'FM09')||to_char(round(dbms_random.value(1,28)), 'FM09');
   w_ins(i).date3 := to_date(round(dbms_random.value(2019,2021))||'-'||round(dbms_random.value(1,12))||'-'||round(dbms_random.value(1,28))||' '||round(dbms_random.value(1, 23))||':'||round(dbms_random.value(0, 59))||':'||round(dbms_random.value(0, 59)), 'YYYY-MM-DD HH24:MI:SS');
   w_ins(i).phone := '010-'||ceil(dbms_random.value(1000, 9999))||'-'||ceil(dbms_random.value(1000, 9999));
   w_ins(i).price := ceil(dbms_random.value(1, 10))*1000;
   w_ins(i).qty   := ceil(dbms_random.value(1, 10));
   w_ins(i).test1 := 1234;
   w_ins(i).test2 := 'SQLP';
   w_ins(i).test3 := 'A'||ceil(dbms_random.value(100, 999));
END LOOP;
FORALL i in 1..100000 INSERT INTO sample_t VALUES w_ins(i);
   COMMIT;
END LOOP;
END;
/
 
PL/SQL procedure successfully completed.

 

 

정확한 리두 생성량 측정을 위해 sqlplus 재접속

1
2
SQL> exit
$ sqlplus  / as sysdba

 

 

샘플 테이블 전체에서 50%만 업데이트

1
2
3
4
5
SQL>
update sample_t set id1=999 
where rownum <= 500000;
 
500000 rows updated.

 

 

리두 생성량 확인(redo.sql)

1
2
3
4
5
6
7
8
9
SQL>
select round(vm.value/1024, 2) "REDO_KB", round(vm.value/1024/1024, 2) "REDO_MB"
from v$statname vs, v$mystat vm
where vs.statistic# = vm.statistic#
and vs.name = 'redo size';
 
   REDO_KB    REDO_MB
---------- ----------
 245850.59     240.09

240mb의 redo가 생성됨

 

 

2. 핫백업(begin 백업) 실행시 redo 발생량 측정

샘플 테이블 제거

1
2
3
SQL> drop table sample_t purge;
 
Table dropped.

 

 

샘플 테이블 재생성

1
2
3
4
5
6
7
SQL>
create table sample_t 
(id1 number, id2 number, name varchar2(10),  
date1 varchar2(8), date2 varchar2(8), date3 date, 
phone varchar2(13), price number, qty number, 
test1 number,  test2 varchar2(5), test3 varchar2(4)
);

 

 

데이터 삽입(100만건)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
SQL>
set timing on
DECLARE
TYPE tbl_ins IS TABLE OF sample_t%ROWTYPE INDEX BY BINARY_INTEGER;
w_ins tbl_ins;
BEGIN
FOR d IN 1..10 LOOP
FOR i IN 1..100000 LOOP 
   w_ins(i).id1   := (d - 1) * 100000 + i;
   w_ins(i).id2   := i||ceil(dbms_random.value(1, 10000000));
   w_ins(i).name  := dbms_random.string('x',5);
   w_ins(i).date1 := round(dbms_random.value(2010,2021))||to_char(round(dbms_random.value(1,12)), 'FM09')||to_char( round(dbms_random.value(1,28)), 'FM09');
   w_ins(i).date2 := '2021'||to_char(round(dbms_random.value(1,12)) , 'FM09')||to_char(round(dbms_random.value(1,28)), 'FM09');
   w_ins(i).date3 := to_date(round(dbms_random.value(2019,2021))||'-'||round(dbms_random.value(1,12))||'-'||round(dbms_random.value(1,28))||' '||round(dbms_random.value(1, 23))||':'||round(dbms_random.value(0, 59))||':'||round(dbms_random.value(0, 59)), 'YYYY-MM-DD HH24:MI:SS');
   w_ins(i).phone := '010-'||ceil(dbms_random.value(1000, 9999))||'-'||ceil(dbms_random.value(1000, 9999));
   w_ins(i).price := ceil(dbms_random.value(1, 10))*1000;
   w_ins(i).qty   := ceil(dbms_random.value(1, 10));
   w_ins(i).test1 := 1234;
   w_ins(i).test2 := 'SQLP';
   w_ins(i).test3 := 'A'||ceil(dbms_random.value(100, 999));
END LOOP;
FORALL i in 1..100000 INSERT INTO sample_t VALUES w_ins(i);
   COMMIT;
END LOOP;
END;
/
 
PL/SQL procedure successfully completed.

 

 

정확한 리두 생성량 측정을 위해 sqlplus 재접속

1
2
SQL> exit
$ sqlplus  / as sysdba

 

 

핫백업 수행

1
2
3
SQL> alter database begin backup;
 
Database altered.

 

 

샘플 테이블 전체에서 50%만 업데이트

1
2
3
4
5
SQL>
update sample_t set id1=999 
where rownum <= 500000;
 
500000 rows updated.

 

 

리두 생성량 확인(redo.sql)

1
2
3
4
5
6
7
8
9
SQL>
select round(vm.value/1024, 2) "REDO_KB", round(vm.value/1024/1024, 2) "REDO_MB"
from v$statname vs, v$mystat vm
where vs.statistic# = vm.statistic#
and vs.name = 'redo size';
 
   REDO_KB    REDO_MB
---------- ----------
 291213.79     284.39

284mb의 redo가 생성됨(기존보다 44mb나 더 발생함)

 

 

핫백업 종료

1
2
3
SQL> alter database end backup;
 
Database altered.

 

 

테스트 결과 : 동일한 dml 구문을 핫백업 실행시와 미실행시 각각 수행해봤음, 핫백업 실행시 redo가 더 많이 발생됨

 

 

이유는?
What Happens When A Tablespace/Database Is Kept In Begin Backup Mode (Doc ID 469950.1)에서 자세히 설명해주고 있음
1. 변경된 부분만 redo에 기록되는게 아니라 블록 전체가 redo에 기록됨
데이터파일이 hot backup 모드에 들어가서 블록이 처음 변경될 때는 바뀐 바이트(redo vector)만 기록하는 게 아니라, 블록 전체를 redo log 파일에 기록함
보통은 바뀐 부분만 기록하지만, hot backup 모드에서는 전체 블록을 기록함
그 이유는 백업 프로그램이 데이터파일을 읽고 있는 동안 dbwr이 동시에 같은 블록을 디스크에 다시 쓰는 상황이 발생할 수 있기 때문임

 

예를 들어 아래와 같은 상황이 발생할 수 있음
a. os 블록 크기가 512바이트이고 oracle 블록 크기가 8kb라 하면
b. 백업 프로그램이 8kb 블록을 읽으려 함
c. os가 먼저 4kb를 줌
d. 그 순간 dbwr이 이 블록 전체 8kb를 다시 쓰도록 스케줄링됨
e. 이어서 백업 프로그램이 나머지 4kb를 읽음
이 경우 백업 프로그램이 읽은 블록은 앞부분(4kb)과 뒷부분(4kb)이 서로 다른 시점의 데이터여서 fractured block(균열이 생긴 블록)이 됨
이렇게 깨진 블록은 복구할 수 없음
그래서 redo에 블록 전체 이미지를 기록해 둬서 복구 시 redo를 이용해 블록 전체를 다시 쓸 수 있도록 함
이렇게 하면 최소한 블록 자체는 내부적으로 일관성을 유지할 수 있음

 

 

2. 데이터파일 헤더 scn 관리
hot backup 모드에 있는 동안은 데이터파일 헤더(최근 체크포인트 scn을 포함하는 부분)가 업데이트되지 않음
이렇게 해야 복구 시 어떤 아카이브 로그가 필요한지 정확히 알 수 있음

 

 

3. 추가 로깅 최소화 방법
위에서 말한것 처럼 hot backup 모드에서는 변경 블록 전체가 redo에 기록되므로, redo 발생량이 평소보다 많아짐, 이를 줄이는 방법은 다음과 같음
a. 한 번에 하나의 tablespace만 백업 모드에 넣고, 백업이 끝나는 대로 바로 backup 모드를 해제
b. 데이터베이스에 부하가 적거나 없는 시간대에 hot/online backup을 수행

 

 

참조 : 

What Happens When A Tablespace/Database Is Kept In Begin Backup Mode (Doc ID 469950.1)
https://positivemh.tistory.com/337
https://positivemh.tistory.com/808