데이터베이스 연구실.log

PDB 시점 복구와 TSPITR

PDB 시점 복구, 보장 복원 지점, 테이블스페이스 TSPITR과 RECOVER TABLE의 조건 및 복구 범위 비교

INSTANCES=ALLSET UNTIL RESTORE POINTRECOVER TABLEREMAP TABLEPDB PITRSET UNTIL SCNOPEN RESETLOGSV$PDB_INCARNATIONFLASHBACK PLUGGABLE DATABASETSPITRAUXILIARY DESTINATIONTRANSPORT_SET_CHECK

#핵심 요약

PDB 시점 복구는 그 PDB만 닫고 루트에서 SET UNTIL SCN과 함께 RESTORE/RECOVER PLUGGABLE DATABASE를 실행한 뒤 OPEN RESETLOGS로 엶
다른 PDB와 CDB는 계속 열어 둘 수 있음

Flashback Pluggable Database는 백업을 복원하지 않고 플래시백 로그로 PDB를 되감으므로 PDB 시점 복구보다 빠름
CDB의 플래시백과 PDB 복원 지점이 필요함

TSPITR은 PDB 안의 테이블스페이스 하나만 과거로 되돌림
보조 인스턴스 위치(AUXILIARY DESTINATION)가 필요하고, 끝나면 테이블스페이스가 OFFLINE으로 남으므로 새로 백업한 뒤 온라인으로 올림

범위가 클수록 함께 과거로 가는 정상 데이터도 많음
테이블 하나만 문제면 RECOVER TABLE ... OF PLUGGABLE DATABASE로 그 테이블만 꺼냄

#개념과 동작 원리

PDB 시점 복구 때 RMAN은 루트에 SYSDBA 또는 SYSBACKUP 공통 사용자로 접속하고, 복구할 PDB만 닫음
나머지 PDB와 CDB는 열려 있어도 됨

CDB가 local undo면 PDB 플래시백은 그 PDB 데이터 파일만 바꾸면 되므로 간단함
shared undo면 보조 인스턴스를 쓰며 기본 위치는 FRA임

PDB 복원 지점은 만든 PDB에서만 쓸 수 있음
보장 PDB 복원 지점은 CDB의 플래시백 로그 재사용을 막아 FRA가 가득 찰 수 있으므로 신중히 만듦

PDB 백업은 PDB 플래시백 뒤에도 유효함
PDB 백업이 자기 완결적인 것은 local undo일 때뿐임(백업 보존과 정리의 KEEP 설명)

방법 과거로 가는 범위
CDB 전체 시점 복구 루트와 모든 PDB
PDB 시점 복구 PDB 하나 전체
TSPITR PDB의 테이블스페이스 하나(그 안의 모든 테이블)
RECOVER TABLE 테이블 하나

TSPITR은 잘못된 작업이 같은 테이블스페이스의 여러 테이블에 걸쳐 있을 때 쓰고, 그 시점 이후 같은 테이블스페이스에 들어온 정상 데이터도 함께 사라짐
CDB에서는 PDB마다 같은 이름의 테이블스페이스(USERS 등)가 있을 수 있어 <PDB_NAME>:<TABLESPACE>로 구분함(non-CDB는 테이블스페이스 이름만 씀)

방법 범위 백업 복원 조건
PDB 시점 복구 PDB 전체 함 대상 시점 이전 백업
shared undo면 AUXILIARY DESTINATION
Flashback PDB PDB 전체 안 함 CDB 플래시백 켜짐, PDB 복원 지점
TSPITR PDB 안의 테이블스페이스 함(보조 인스턴스) 테이블스페이스 자기 완결성, AUXILIARY DESTINATION

#필수 명령어 또는 SQL

#PDB 시점 복구

SQL*Plus (SYSDBA, CDB$ROOT)조회만 함
SELECT property_value FROM database_properties WHERE property_name = 'LOCAL_UNDO_ENABLED';
ARCHIVE LOG LIST

결과 해석:

실습 기록: LOCAL_UNDO_ENABLED는 TRUE였음
실습에서는 FALSE면 AUXILIARY DESTINATION을 붙이라고 적었음

RMAN (CDB$ROOT)객체 또는 설정을 변경함
BACKUP PLUGGABLE DATABASE <PDB_NAME> PLUS ARCHIVELOG;
SQL*Plus (SYSDBA)조회만 함
SELECT current_scn FROM v$database;

결과 해석:

실습에서는 되돌릴 기준 SCN을 기록한 뒤 테이블을 DROP ... PURGE로 지웠음
휴지통을 거치지 않으므로 Flashback Drop으로는 되살릴 수 없는 상황임

SQL*Plus (SYSDBA, CDB$ROOT)인스턴스 또는 서비스 중단이 필요함
ALTER PLUGGABLE DATABASE <PDB_NAME> CLOSE IMMEDIATE;
RMAN (CDB$ROOT)객체 또는 설정을 변경함
RUN {
  SET UNTIL SCN <SCN>;
  RESTORE PLUGGABLE DATABASE <PDB_NAME>;
  RECOVER PLUGGABLE DATABASE <PDB_NAME>;
}
SQL*Plus (SYSDBA, CDB$ROOT)객체 또는 설정을 변경함
ALTER PLUGGABLE DATABASE <PDB_NAME> OPEN RESETLOGS;
SQL*Plus (SYSDBA, CDB$ROOT)조회만 함
SELECT con_id, pdb_incarnation#, status, incarnation_time
  FROM v$pdb_incarnation WHERE con_id = <CON_ID> ORDER BY pdb_incarnation#;

결과 해석:

설명: 리두 로그는 CDB 전체가 함께 쓰므로 PDB를 OPEN RESETLOGS해도 CDB의 로그 시퀀스는 1로 돌아가지 않고 이어짐
새로 생기는 것은 그 PDB만의 incarnation이며 V$DATABASE_INCARNATION이 아니라 V$PDB_INCARNATION에서 확인함
새 PDB incarnation이 CURRENT로 추가됨
CDB 루트는 복구 동안 READ WRITE 상태였음
실습에서는 복구 뒤 로그 시퀀스가 "처음 기록과 같거나 이어지는 번호"라고 적었지만, 처음 기록은 34이고 복구 뒤 기록은 15였음

#RAC에서 PDB 시점 복구

SQL*Plus (복구할 노드, CDB$ROOT SYSDBA)인스턴스 또는 서비스 중단이 필요함
ALTER PLUGGABLE DATABASE <PDB_NAME> CLOSE IMMEDIATE INSTANCES = ALL;
SELECT inst_id, name, open_mode FROM gv$pdbs WHERE name = '<PDB_NAME>';
RMAN (복구할 노드)데이터 또는 파일을 삭제함, 되돌릴 수 없음
RUN {
  SET UNTIL TIME "TO_DATE('<YYYY-MM-DD HH24:MI:SS>', 'YYYY-MM-DD HH24:MI:SS')";
  -- 또는 SET UNTIL RESTORE POINT <RESTORE_POINT>;
  RESTORE PLUGGABLE DATABASE <PDB_NAME>;
  RECOVER PLUGGABLE DATABASE <PDB_NAME>;
}
ALTER PLUGGABLE DATABASE <PDB_NAME> OPEN RESETLOGS;
SQL*Plus (CDB$ROOT SYSDBA)객체 또는 설정을 변경함
ALTER PLUGGABLE DATABASE <PDB_NAME> OPEN INSTANCES = ('<OTHER_INSTANCE>');

결과 해석:

복원, 리두 적용 동안 다른 인스턴스가 같은 PDB 파일을 쓰지 않도록 INSTANCES = ALL로 모든 노드에서 닫음
CDB 루트와 다른 PDB는 계속 열어 둠

OPEN RESETLOGS는 복구한 인스턴스에서 한 번만 하고, 나머지 인스턴스는 일반 OPEN으로 엶

실습 기록: 2번 노드에서 DROP TABLE t_pitr PURGE 뒤 ALTER SYSTEM ARCHIVE LOG CURRENT로 모든 thread를 아카이브하고, 1번 노드에서 기록해 둔 시각으로 복구하자 2번 노드에서 500건이 다시 보였음
로컬 UNDO 모드라 RMAN 출력에 보조 인스턴스 생성 과정이 없었고, CDB incarnation은 그대로 두고 그 PDB의 incarnation만 새로 CURRENT가 됐음

데이터 파일, 컨트롤 파일, 아카이브가 공유 저장소(+DATA, +FRA)에 있어 실수한 노드와 다른 노드에서도 복구할 수 있음

연습 문제: 2번 노드의 월말 배치가 t_order를 TRUNCATE했고 배치 직전에 루트에서 CREATE RESTORE POINT before_close_batch를 만들어 둔 상황에서, 정확한 시각 없이 SET UNTIL RESTORE POINT로 2번 노드에서만 복구하고 두 인스턴스에서 1,000건을 확인함

#Flashback Pluggable Database

SQL*Plus (SYSDBA, CDB$ROOT)객체 또는 설정을 변경함
SELECT flashback_on FROM v$database;
ALTER DATABASE FLASHBACK ON;

결과 해석:

설명: ARCHIVELOG 모드이고 FRA가 설정되어 있으면 열린 상태에서 바로 켤 수 있음
플래시백 로그는 FRA의 flashback 디렉터리에 쌓임

SQL*Plus (SYSDBA)객체 또는 설정을 변경함
ALTER SESSION SET CONTAINER = <PDB_NAME>;
CREATE RESTORE POINT before_del GUARANTEE FLASHBACK DATABASE;
SELECT name, con_id, guarantee_flashback_database, scn FROM v$restore_point;

결과 해석:

설명: PDB 안에서 만든 복원 지점은 그 PDB 전용임

SQL*Plus (SYSDBA, CDB$ROOT)인스턴스 또는 서비스 중단이 필요함
ALTER PLUGGABLE DATABASE <PDB_NAME> CLOSE IMMEDIATE;
FLASHBACK PLUGGABLE DATABASE <PDB_NAME> TO RESTORE POINT before_del;
ALTER PLUGGABLE DATABASE <PDB_NAME> OPEN RESETLOGS;
SQL*Plus (SYSDBA)데이터 또는 파일을 삭제함, 되돌릴 수 없음
ALTER SESSION SET CONTAINER = <PDB_NAME>;
DROP RESTORE POINT before_del;
ALTER SESSION SET CONTAINER = CDB$ROOT;
ALTER DATABASE FLASHBACK OFF;

결과 해석:

설명: 보장 복원 지점을 지우지 않으면 플래시백 로그가 FRA에 계속 쌓여 가득 찰 수 있음
공식 문서도 같은 위험을 적음

#PDB 안의 테이블스페이스만 되돌리기 (TSPITR)

실습에서는 테이블과 인덱스를 같은 테이블스페이스로 옮기고(ALTER TABLE ... MOVE, ALTER INDEX ... REBUILD), 두 번째 테이블을 만든 뒤 백업하고 SCN을 기록했음
그 다음 한 테이블에는 정상 행을 넣고 다른 테이블의 행을 실수로 지웠음

SQL*Plus (SYSDBA, 대상 PDB)조회만 함
EXEC DBMS_TTS.TRANSPORT_SET_CHECK('<TABLESPACE_NAME>', TRUE);
SELECT * FROM transport_set_violations;

SELECT owner, name, creation_time
  FROM ts_pitr_objects_to_be_dropped
 WHERE tablespace_name = '<TABLESPACE_NAME>'
   AND creation_time > SCN_TO_TIMESTAMP(<SCN>);

결과 해석:

설명: TRANSPORT_SET_VIOLATIONS에 행이 나오면 다른 테이블스페이스 객체와 얽혀 있어 TSPITR을 할 수 없음(예: 인덱스만 다른 테이블스페이스에 있음)
TS_PITR_OBJECTS_TO_BE_DROPPED에 나오는 객체는 그 시점 이후에 만들어져 TSPITR 뒤 사라지므로 필요하면 미리 Data Pump로 빼 둠
실습 기록: violations는 "no rows selected"였음

RMAN (CDB$ROOT)객체 또는 설정을 변경함
RECOVER TABLESPACE <PDB_NAME>:<TABLESPACE_NAME>
  UNTIL SCN <SCN>
  AUXILIARY DESTINATION '<AUX_DIR>';

BACKUP TABLESPACE <PDB_NAME>:<TABLESPACE_NAME>;
SQL*Plus (SYSDBA, 대상 PDB)객체 또는 설정을 변경함
SELECT tablespace_name, status FROM dba_tablespaces WHERE tablespace_name = '<TABLESPACE_NAME>';
ALTER TABLESPACE <TABLESPACE_NAME> ONLINE;

결과 해석:

설명: TSPITR 동안 루트와 그 PDB의 다른 테이블스페이스는 계속 서비스됨
끝나면 테이블스페이스가 OFFLINE으로 남고 이전 백업은 더 쓸 수 없으므로 온라인으로 올리기 전에 새로 백업함
실습 기록: 실수로 지운 행은 돌아왔고, 같은 테이블스페이스의 다른 테이블에 기준 SCN 뒤에 넣은 정상 행은 함께 사라졌음

TSPITR 관련 공식 문서 문장은 확인 불가

#PDB 안의 테이블 하나만 복구 (RECOVER TABLE)

DROP TABLE ... PURGE로 지워 휴지통에서도 되살릴 수 없거나, 데이터만 잘못 지운 테이블 하나를 과거 시점으로 꺼낼 때 씀
RMAN이 보조 인스턴스를 만들어 그 시점까지 복원, 복구한 뒤 Data Pump로 테이블을 내보내고 가져옴

동작 순서: AUXILIARY DESTINATION에 보조 인스턴스 생성, 루트와 PDB의 SYSTEM, SYSAUX, UNDO와 테이블이 있는 테이블스페이스를 복원, UNTIL 시점까지 복구, Data Pump로 내보내 운영 PDB에 가져오기, 보조 인스턴스와 파일 자동 삭제

non-CDB에서는 OF PLUGGABLE DATABASE 없이 RECOVER TABLE <OWNER>.<TABLE_NAME> UNTIL SCN ... AUXILIARY DESTINATION ...으로 씀

쓸 수 없는 경우: ARCHIVELOG 모드가 아니거나 되돌릴 시점 이전 백업이 없을 때, SYS, SYSTEM 소유 테이블이나 SYSTEM, SYSAUX 테이블스페이스의 테이블, 보조 인스턴스 위치에 공간(수 GB)이 없을 때

보조 인스턴스에는 루트와 그 PDB의 SYSTEM, SYSAUX, UNDO가 복원되므로 PDB만 백업해서는 부족하고 CDB 전체 백업이 필요함

실습 자료는 보조 인스턴스 위치에 최소 5GB 이상 여유 공간(TEMP 테이블스페이스 공간 포함)을 두고, 보조 인스턴스도 메모리를 쓰므로 다른 DB는 내려 두라고 적었음

OS 셸 (oracle 사용자)객체 또는 설정을 변경함
mkdir -p <AUX_DIR>
df -h <AUX_DIR>
SQL*Plus (SYSDBA, 대상 PDB)조회만 함
SELECT owner, table_name, tablespace_name FROM dba_tables WHERE owner = '<OWNER>';
SELECT current_scn FROM v$database;                    -- 사고 전 SCN을 적어 둠
SELECT SCN_TO_TIMESTAMP(<SCN>) FROM dual;              -- SCN을 시각으로
SELECT TIMESTAMP_TO_SCN(TO_TIMESTAMP('<YYYY-MM-DD HH24:MI:SS>', 'YYYY-MM-DD HH24:MI:SS')) FROM dual;   -- 시각을 SCN으로
RMAN (CDB$ROOT)객체 또는 설정을 변경함
BACKUP DATABASE PLUS ARCHIVELOG;

RECOVER TABLE <OWNER>.<TABLE_NAME> OF PLUGGABLE DATABASE <PDB_NAME>
  UNTIL SCN <SCN>
  AUXILIARY DESTINATION '<AUX_DIR>';

결과 해석:

OF PLUGGABLE DATABASE를 빼면 루트에서 테이블을 찾다가 실패함

진행 순서: 보조 인스턴스 생성, restore, recover, Performing export of tables, Performing import of tables, Removing automatic instance 순서로 출력됨
복구 동안 그 PDB는 계속 열려 있고 다른 테이블은 영향이 없음

결과 확인: 사고 전에 적어 둔 건수와 같은지 보고, <AUX_DIR>의 보조 인스턴스 파일이 정리됐는지 확인함

테이블이 아직 남아 있고 데이터만 지워졌다면 같은 이름으로 복구하면 충돌하므로 다른 이름으로 꺼내 필요한 행만 되넣음

RMAN (CDB$ROOT)객체 또는 설정을 변경함
RECOVER TABLE <OWNER>.<TABLE_NAME> OF PLUGGABLE DATABASE <PDB_NAME>
  UNTIL SCN <SCN>
  AUXILIARY DESTINATION '<AUX_DIR>'
  REMAP TABLE '<OWNER>'.'<TABLE_NAME>':'<TABLE_NAME>_OLD';
SQL*Plus (대상 PDB의 테이블 소유자)데이터 또는 파일을 삭제함, 되돌릴 수 없음
INSERT INTO <TABLE_NAME>
SELECT * FROM <TABLE_NAME>_OLD
 WHERE <KEY_COLUMN> NOT IN (SELECT <KEY_COLUMN> FROM <TABLE_NAME>);
COMMIT;
DROP TABLE <TABLE_NAME>_OLD PURGE;

결과 해석:

REMAP TABLE의 이름은 대문자와 작은따옴표로 적음

운영 PDB에 바로 넣지 않고 덤프 파일만 받으려면 NOTABLEIMPORT와 DATAPUMP DESTINATION '<AUX_DIR>' DUMP FILE '<FILE>.dmp'를 붙임

#사용 조건과 주의사항

세 방법 모두 대상 시점 이후의 변경을 그 범위 안에서 잃음
범위를 먼저 고름

실습에서는 샘플 계정 접속 문자열에 비밀번호를 적었음

SEO_HKabsumveritas@gmail.comBuilt with Claude Code & Codex

마지막 갱신 개인 학습 기록이며 운영 환경 적용 전 공식 문서 확인 필요

두 글자 이상 입력, 위아래 화살표로 이동하고 Enter로 열기