2026-03-12 21:01:38
This commit is contained in:
555
divers/xtts_non-cdb_to_cdb_01.md
Normal file
555
divers/xtts_non-cdb_to_cdb_01.md
Normal file
@@ -0,0 +1,555 @@
|
||||
## 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';
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user