556 lines
14 KiB
Markdown
556 lines
14 KiB
Markdown
## Context
|
|
|
|
- Source: non-CDB = GREEDO@rodia-scan
|
|
- Target: PDB = REEK, CDB=AERONPRD@ylesia-scan
|
|
|
|
## Setup
|
|
|
|
Create tablespaces and users:
|
|
|
|
```
|
|
create tablespace TS1 datafile size 16M autoextend on next 16M;
|
|
create tablespace TS2 datafile size 16M autoextend on next 16M;
|
|
create tablespace TS3 datafile size 16M autoextend on next 16M;
|
|
|
|
alter tablespace TS1 add datafile size 16M autoextend on next 16M;
|
|
alter tablespace TS1 add datafile size 16M autoextend on next 16M;
|
|
alter tablespace TS2 add datafile size 16M autoextend on next 16M;
|
|
alter tablespace TS3 add datafile size 16M autoextend on next 16M;
|
|
alter tablespace TS3 add datafile size 16M autoextend on next 16M;
|
|
alter tablespace TS3 add datafile size 16M autoextend on next 16M;
|
|
|
|
create user U1 identified by secret;
|
|
grant connect, resource, create view,create job to U1;
|
|
alter user U1 quota unlimited on TS1;
|
|
alter user U1 quota unlimited on TS2;
|
|
alter user U1 quota unlimited on TS3;
|
|
|
|
create user U2 identified by secret;
|
|
grant connect, resource, create view,create job to U2;
|
|
alter user U2 quota unlimited on TS1;
|
|
alter user U2 quota unlimited on TS2;
|
|
alter user U2 quota unlimited on TS3;
|
|
```
|
|
|
|
For each user, create objects:
|
|
|
|
connect U1/secret
|
|
-- create objcts
|
|
connect U2/secret
|
|
-- create objcts
|
|
|
|
Create objects script:
|
|
|
|
```
|
|
-- TABLE 1 dans TS1
|
|
CREATE TABLE table1_ts1 (
|
|
id NUMBER PRIMARY KEY,
|
|
data VARCHAR2(100),
|
|
created_at DATE DEFAULT SYSDATE
|
|
) TABLESPACE TS1;
|
|
|
|
CREATE SEQUENCE table1_seq
|
|
START WITH 1
|
|
INCREMENT BY 1
|
|
NOCACHE
|
|
NOCYCLE;
|
|
|
|
CREATE OR REPLACE TRIGGER trg_table1_id
|
|
BEFORE INSERT ON table1_ts1
|
|
FOR EACH ROW
|
|
BEGIN
|
|
IF :NEW.id IS NULL THEN
|
|
SELECT table1_seq.NEXTVAL INTO :NEW.id FROM dual;
|
|
END IF;
|
|
END;
|
|
/
|
|
|
|
-- TABLE 2 dans TS2
|
|
CREATE TABLE table2_ts2 (
|
|
id NUMBER PRIMARY KEY,
|
|
data VARCHAR2(100),
|
|
updated_at DATE
|
|
) TABLESPACE TS2;
|
|
|
|
CREATE SEQUENCE table2_seq
|
|
START WITH 1
|
|
INCREMENT BY 1
|
|
NOCACHE
|
|
NOCYCLE;
|
|
|
|
CREATE OR REPLACE TRIGGER trg_table2_id
|
|
BEFORE INSERT ON table2_ts2
|
|
FOR EACH ROW
|
|
BEGIN
|
|
IF :NEW.id IS NULL THEN
|
|
SELECT table2_seq.NEXTVAL INTO :NEW.id FROM dual;
|
|
END IF;
|
|
END;
|
|
/
|
|
|
|
-- TABLE 3 dans TS3
|
|
CREATE TABLE table3_ts3 (
|
|
id NUMBER PRIMARY KEY,
|
|
info VARCHAR2(100),
|
|
status VARCHAR2(20)
|
|
) TABLESPACE TS3;
|
|
|
|
CREATE SEQUENCE table3_seq
|
|
START WITH 1
|
|
INCREMENT BY 1
|
|
NOCACHE
|
|
NOCYCLE;
|
|
|
|
CREATE OR REPLACE TRIGGER trg_table3_id
|
|
BEFORE INSERT ON table3_ts3
|
|
FOR EACH ROW
|
|
BEGIN
|
|
IF :NEW.id IS NULL THEN
|
|
SELECT table3_seq.NEXTVAL INTO :NEW.id FROM dual;
|
|
END IF;
|
|
END;
|
|
/
|
|
|
|
|
|
CREATE OR REPLACE VIEW combined_view AS
|
|
SELECT id, data, created_at, NULL AS updated_at, NULL AS status FROM table1_ts1
|
|
UNION ALL
|
|
SELECT id, data, updated_at, NULL AS created_at, NULL AS status FROM table2_ts2
|
|
UNION ALL
|
|
SELECT id, info AS data, NULL, NULL, status FROM table3_ts3;
|
|
|
|
|
|
CREATE OR REPLACE PACKAGE data_ops AS
|
|
PROCEDURE insert_random_data;
|
|
PROCEDURE update_random_data;
|
|
PROCEDURE delete_random_data;
|
|
END data_ops;
|
|
/
|
|
|
|
CREATE OR REPLACE PACKAGE BODY data_ops AS
|
|
PROCEDURE insert_random_data IS
|
|
BEGIN
|
|
FOR i IN 1..10 LOOP
|
|
INSERT INTO table1_ts1 (data)
|
|
VALUES (DBMS_RANDOM.STRING('A', 10));
|
|
END LOOP;
|
|
|
|
FOR i IN 1..3 LOOP
|
|
INSERT INTO table3_ts3 (info, status)
|
|
VALUES (DBMS_RANDOM.STRING('A', 10), 'NEW');
|
|
END LOOP;
|
|
END;
|
|
|
|
PROCEDURE update_random_data IS
|
|
BEGIN
|
|
FOR i IN 1..7 LOOP
|
|
INSERT INTO table2_ts2 (data)
|
|
VALUES (DBMS_RANDOM.STRING('A', 10));
|
|
END LOOP;
|
|
FOR rec IN (
|
|
SELECT id FROM (
|
|
SELECT id FROM table2_ts2 ORDER BY DBMS_RANDOM.VALUE
|
|
) WHERE ROWNUM <= 5
|
|
) LOOP
|
|
UPDATE table2_ts2
|
|
SET data = DBMS_RANDOM.STRING('A', 10), updated_at = SYSDATE
|
|
WHERE id = rec.id;
|
|
END LOOP;
|
|
END;
|
|
|
|
PROCEDURE delete_random_data IS
|
|
BEGIN
|
|
FOR rec IN (
|
|
SELECT id FROM (
|
|
SELECT id FROM table3_ts3 ORDER BY DBMS_RANDOM.VALUE
|
|
) WHERE ROWNUM <= 2
|
|
) LOOP
|
|
DELETE FROM table3_ts3 WHERE id = rec.id;
|
|
END LOOP;
|
|
END;
|
|
END data_ops;
|
|
/
|
|
```
|
|
|
|
Create job to run every 1 minute:
|
|
|
|
```
|
|
BEGIN
|
|
DBMS_SCHEDULER.CREATE_JOB (
|
|
job_name => 'random_ops_job',
|
|
job_type => 'PLSQL_BLOCK',
|
|
job_action => '
|
|
BEGIN
|
|
data_ops.insert_random_data;
|
|
data_ops.update_random_data;
|
|
data_ops.delete_random_data;
|
|
END;',
|
|
start_date => SYSTIMESTAMP,
|
|
repeat_interval => 'FREQ=MINUTELY; INTERVAL=1',
|
|
enabled => TRUE,
|
|
comments => 'Job to insert, update and delete random data every minute.'
|
|
);
|
|
END;
|
|
/
|
|
```
|
|
|
|
To restart the job:
|
|
|
|
```
|
|
--Restart the job
|
|
BEGIN
|
|
DBMS_SCHEDULER.enable('random_ops_job');
|
|
END;
|
|
/
|
|
```
|
|
|
|
Count the lines in tables:
|
|
|
|
```
|
|
select
|
|
'u1.table1_ts1:'||count(*) from u1.table1_ts1
|
|
union select
|
|
'u1.table2_ts2:'||count(*) from u1.table2_ts2
|
|
union select
|
|
'u1.table3_ts3:'||count(*) from u1.table3_ts3
|
|
union select
|
|
'u2.table1_ts1:'||count(*) from u2.table1_ts1
|
|
union select
|
|
'u2.table2_ts2:'||count(*) from u2.table2_ts2
|
|
union select
|
|
'u2.table3_ts3:'||count(*) from u2.table3_ts3
|
|
order by 1 asc
|
|
/
|
|
```
|
|
|
|
To ensure the automatic opening of PDB, create a service to start automatically in the PDB:
|
|
|
|
srvctl add service -s adm_reek -db AERONPRD -preferred AERONPRD1,AERONPRD2,AERONPRD3 -pdb REEK -role PRIMARY
|
|
srvctl start service -s adm_reek -db AERONPRD
|
|
|
|
|
|
## XTTS
|
|
|
|
> Note MOS: V4 Reduce Transportable Tablespace Downtime using Cross Platform Incremental Backup (Doc ID 2471245.1)
|
|
|
|
### Initial setup
|
|
|
|
Identify tablespaces to transport, probably all non "administrative" tablespaces:
|
|
|
|
```
|
|
select
|
|
listagg(tablespace_name, ',')
|
|
within group
|
|
(order by tablespace_name) as non_sys_ts
|
|
from
|
|
dba_tablespaces
|
|
where
|
|
contents not in ('UNDO','TEMPORARY') and
|
|
tablespace_name not in ('SYSTEM','SYSAUX');
|
|
```
|
|
|
|
For source and target servers, define folders to be used for scripts, backupset, datapump etc.
|
|
In our case, that will be a shared NFS folder `/mnt/unprotected/tmp/oracle/xtts`
|
|
|
|
> The size of folder should be greather than the size of full database.
|
|
|
|
Unzip xtts scripts:
|
|
|
|
cd /mnt/unprotected/tmp/oracle/xtts
|
|
unzip /mnt/yavin4/kit/Oracle/XTTS/rman_xttconvert_VER4.3.zip
|
|
|
|
Configure xtt.properties file:
|
|
|
|
```
|
|
tablespaces=TS1,TS2,TS3,USERS
|
|
src_scratch_location=/mnt/unprotected/tmp/oracle/xtts/scratch
|
|
dest_datafile_location=+DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/xtts/
|
|
dest_scratch_location=/mnt/unprotected/tmp/oracle/xtts/scratch
|
|
asm_home=/app/oracle/grid/product/19
|
|
asm_sid=+ASM1
|
|
destconnstr=sys/"Secret00!"@ylesia-scan/adm_reek
|
|
usermantransport=1
|
|
```
|
|
|
|
On target server, create ASM directory where the datafile will be restored:
|
|
|
|
mkdir +DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/xtts
|
|
|
|
On **both source and target** servers, set `TMPDIR` environment variable to the path of xtts scripts:
|
|
|
|
export TMPDIR=/mnt/unprotected/tmp/oracle/xtts
|
|
|
|
### Prepare Phase
|
|
|
|
This step corresponds to initial full backup/restore of source database on target system.
|
|
|
|
Initial backup on source server:
|
|
|
|
```
|
|
export TMPDIR=/mnt/unprotected/tmp/oracle/xtts
|
|
cd $TMPDIR
|
|
$ORACLE_HOME/perl/bin/perl xttdriver.pl --backup --debug 3
|
|
```
|
|
|
|
Initial restore on target server:
|
|
|
|
```
|
|
export TMPDIR=/mnt/unprotected/tmp/oracle/xtts
|
|
cd $TMPDIR
|
|
$ORACLE_HOME/perl/bin/perl xttdriver.pl --restore --debug 3
|
|
```
|
|
|
|
> `debug` argument is optional
|
|
|
|
### Roll Forward Phase
|
|
|
|
As long as necessary we can do incremental backup/resore operations.
|
|
|
|
> New datafiles add to source database are automatically managed by this step.
|
|
|
|
The commands are exactly the sames (with or without debug mode).
|
|
|
|
For backup:
|
|
|
|
```
|
|
export TMPDIR=/mnt/unprotected/tmp/oracle/xtts
|
|
cd $TMPDIR
|
|
$ORACLE_HOME/perl/bin/perl xttdriver.pl --backup
|
|
```
|
|
|
|
For restore:
|
|
|
|
```
|
|
export TMPDIR=/mnt/unprotected/tmp/oracle/xtts
|
|
cd $TMPDIR
|
|
$ORACLE_HOME/perl/bin/perl xttdriver.pl --restore
|
|
```
|
|
|
|
> Running succesives backup or successive restore operations does not pose a problem.
|
|
|
|
### Final Incremental Backup
|
|
|
|
On **source** database, put tablespaces in **read-only** mode:
|
|
|
|
```
|
|
select
|
|
'alter tablespace '||tablespace_name||' read only;' as COMMAND
|
|
from
|
|
dba_tablespaces
|
|
where
|
|
contents not in ('UNDO','TEMPORARY') and
|
|
tablespace_name not in ('SYSTEM','SYSAUX');
|
|
```
|
|
|
|
Check:
|
|
|
|
```
|
|
select distinct status
|
|
from
|
|
dba_tablespaces
|
|
where
|
|
contents not in ('UNDO','TEMPORARY') and
|
|
tablespace_name not in ('SYSTEM','SYSAUX');
|
|
```
|
|
|
|
Take final incremental backup:
|
|
|
|
```
|
|
export TMPDIR=/mnt/unprotected/tmp/oracle/xtts
|
|
cd $TMPDIR
|
|
$ORACLE_HOME/perl/bin/perl xttdriver.pl --backup
|
|
```
|
|
|
|
Rstore final incremental backup:
|
|
|
|
```
|
|
export TMPDIR=/mnt/unprotected/tmp/oracle/xtts
|
|
cd $TMPDIR
|
|
$ORACLE_HOME/perl/bin/perl xttdriver.pl --restore
|
|
```
|
|
|
|
### Metadata export
|
|
|
|
Create DATAPUMP directory on **both** source and destination databases.
|
|
On source (non-CDB):
|
|
|
|
SQL> create or replace directory XTTS as '/mnt/unprotected/tmp/oracle/xtts';
|
|
|
|
On destination (PDB):
|
|
|
|
export ORACLE_PDB_SID=REEK
|
|
SQL> create or replace directory XTTS as '/mnt/unprotected/tmp/oracle/xtts';
|
|
|
|
Export metadata
|
|
|
|
expdp userid="'/ as sysdba'" dumpfile=XTTS:metadata.dmp logfile=XTTS:metadata.log FULL=y TRANSPORTABLE=always
|
|
|
|
### Optionally: on target, pout target datafiles read-only at OS level
|
|
|
|
Identify OMF target datafiles:
|
|
|
|
```
|
|
asmcmd -p
|
|
cd +DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/xtts
|
|
ls --permission
|
|
```
|
|
|
|
For each datafile, set read-olny permisions, example:
|
|
|
|
chmod 444 +DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/xtts/*
|
|
|
|
If you got:
|
|
|
|
ORA-15304: operation requires ACCESS_CONTROL.ENABLED attribute to be TRUE (DBD ERROR: OCIStmtExecute)
|
|
|
|
then set following diskgroup attributes and retry.
|
|
|
|
```
|
|
column dg_name format a20
|
|
column name format a50
|
|
column VALUE format a30
|
|
|
|
set lines 120
|
|
|
|
select
|
|
dg.name dg_name, attr.name, attr.value
|
|
from
|
|
v$asm_attribute attr
|
|
join v$asm_diskgroup dg on attr.group_number=dg.group_number
|
|
where
|
|
attr.name in ('compatible.rdbms','access_control.enabled')
|
|
order by dg.name, attr.name
|
|
/
|
|
|
|
|
|
alter diskgroup DATA set attribute 'compatible.rdbms' = '19.0.0.0.0';
|
|
alter diskgroup RECO set attribute 'compatible.rdbms' = '19.0.0.0.0';
|
|
|
|
alter diskgroup DATA set attribute 'access_control.enabled' = 'TRUE';
|
|
alter diskgroup RECO set attribute 'access_control.enabled' = 'TRUE';
|
|
```
|
|
|
|
> Compare number of datafiles transported and the number of datafiles of non-Oracle tablespaces
|
|
> Check if transported tablespaces already exists on target database
|
|
|
|
### Metadata import and tablespace plug-in
|
|
|
|
Create impdp parfile `impo_metadata.par`:
|
|
|
|
```
|
|
userid="/ as sysdba"
|
|
dumpfile=XTTS:metadata.dmp
|
|
logfile=XTTS:impo_metadata.log
|
|
transport_datafiles=
|
|
+DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/DATAFILE/TS1.290.1205059373,
|
|
+DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/DATAFILE/TS1.291.1205059373,
|
|
+DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/DATAFILE/TS1.298.1205060113,
|
|
+DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/DATAFILE/TS1.289.1205059373,
|
|
+DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/DATAFILE/TS2.293.1205059375,
|
|
+DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/DATAFILE/TS2.300.1205060113,
|
|
+DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/DATAFILE/TS2.292.1205059375,
|
|
+DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/DATAFILE/TS3.294.1205059381,
|
|
+DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/DATAFILE/TS3.295.1205059381,
|
|
+DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/DATAFILE/TS3.296.1205059381,
|
|
+DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/DATAFILE/TS3.297.1205059381,
|
|
+DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/DATAFILE/TS3.299.1205060113,
|
|
+DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/DATAFILE/USERS.302.1205084171
|
|
```
|
|
|
|
Run import:
|
|
|
|
impdp parfile=impo_metadata.par
|
|
|
|
|
|
Rebounce the PDB (or the CDB), otherwise we can get errors like:
|
|
|
|
```
|
|
ORA-01114: IO error writing block to file 33 (block # 1)
|
|
ORA-01110: data file 33:
|
|
'+DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/DATAFILE/ts1.298.1205060113'
|
|
ORA-27009: cannot write to file opened for read
|
|
```
|
|
|
|
Put plugged tablespaces in read/write mode:
|
|
|
|
```
|
|
select
|
|
'alter tablespace '||tablespace_name||' read write;' as COMMAND
|
|
from
|
|
dba_tablespaces
|
|
where
|
|
contents not in ('UNDO','TEMPORARY') and
|
|
tablespace_name not in ('SYSTEM','SYSAUX');
|
|
```
|
|
|
|
Remove aliases in order to user only OMF datafiles:
|
|
|
|
```
|
|
cd +DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/xtts
|
|
rmalias ts1_8.dbf ts2_13.dbf... .... ...
|
|
cd ..
|
|
rm -rf xtts
|
|
```
|
|
|
|
## Unxexpectd issues
|
|
|
|
In metadata import step I relize I forgot to include USER tablespace in `xtt.properties` and impdp failed wit error:
|
|
|
|
ORA-39352: Wrong number of TRANSPORT_DATAFILES specified: expected 13, received 12
|
|
|
|
The tablespace USER being in read-only mode I copied the datafile manually on target database.
|
|
|
|
Identify the file number:
|
|
|
|
```
|
|
SQL> select FILE_ID from dba_data_files where TABLESPACE_NAME='USERS';
|
|
|
|
FILE_ID
|
|
----------
|
|
7
|
|
```
|
|
|
|
Backup datafile on source:
|
|
|
|
```
|
|
run{
|
|
set nocfau;
|
|
backup datafile 7 format '/mnt/unprotected/tmp/oracle/xtts/%d_%U_%s_%t.bck';
|
|
}
|
|
```
|
|
|
|
Restore datafile on target;
|
|
|
|
```
|
|
run {
|
|
restore from platform 'Linux x86 64-bit'
|
|
foreign datafile 7 format '+DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/xtts//USERS.dbf'
|
|
from backupset '/mnt/unprotected/tmp/oracle/xtts/GREEDO_0i3t87ss_18_1_1_18_1205084060.bck';
|
|
}
|
|
```
|
|
|
|
Put datafile in read-ony at ASM level:
|
|
|
|
chmod 444 +DATA/AERONPRD/389011A6CB11A654E0635000A8C07D80/DATAFILE/USERS.302.1205084171
|
|
|
|
Run the impdp again.
|
|
|
|
|
|
## Troubleshooting
|
|
|
|
Having datafile to plug-in in read-only mode at ASM level allow to repeat tne impdp operations as many time as necessary.
|
|
For example, to completly re-execute the impdp metadata as on initial conditions:
|
|
- drop new plugged tablespaces
|
|
- drop non oracle maintened users
|
|
- run impdp metadata again
|
|
|
|
```
|
|
drop tablespace TS1 including contents;
|
|
drop tablespace TS2 including contents;
|
|
drop tablespace TS3 including contents;
|
|
drop tablespace USERS including contents;
|
|
|
|
select 'drop user '||USERNAME||' cascade;' from dba_users where ORACLE_MAINTAINED='N';
|
|
```
|
|
|