Oracle 19C 热克隆应用避坑指南

本文由 dbaplus 社群授权转载。

一、背景

随着 Oracle 11g 进入扩展支持阶段,Oracle 19C 作为 12C 家族中最终稳定版,已被多数公司熟知及应用于生产。本人所在公司也在尝试对 19C 进行部署、测试、升级、迁移,于是借此机会将热克隆这个特性做了一番测试。

二、使用热克隆的前提

1、需要 12C R2 及以上版本

在 12C R1 中,要克隆 PDB,源 PDB 必须在克隆操作期间处于静止状态,因此它需要源 PDB 停机,通俗的讲这是种“冷克隆”。

从 12C R2 及以后的版本中开始支持“热克隆”,即 Oracle 数据库支持使用联机克隆的功能。当源 PDB 以读写方式打开的状态下,完全不需要中断源 PDB 中的操作,无须应用程序停机,就可以进行克隆操作。

2、必须使用 local undo

当使用 share undo 的情况下,需要将 share undo 转成 local undo 后才可以使用热克隆。可以在 upgrade 模式下用 alter database local undo on 进行转换。

三、工作原理

从下面三张图可以看出不管是本地克隆、远程克隆,还是 non-cdb 克隆,都是类似 rman 方式进行备份恢复。热克隆会有以下 3 个阶段:

第一阶段:当热克隆开始时(t0),对源 PDB 的数据文件按块进行读取,直到源 PDB 最后一个块被读取并将其复制到目标 PDB 时(t1),此时 t0-t1 时间段内可能对已经复制的一些块进行了更改。那么,在此阶段,目标 PDB 可能与源 PDB 在物理上不一致。

第二阶段:将 t0-t1 之间对源 PDB 所做的更改传至目标 PDB,进行重做应用。在此阶段,目标 PDB 将成为 t1 时源 PDB 的物理副本,但这里即包括了已提交的事务,也包括未提交的事务,因此可能在事务上不一致。

第三阶段:截止至 t1 时,源 PDB 中包含所有已提交的事务,所有未提交的事务将进行回滚,目标 PDB 将是截至 t1 时源 PDB 的事务一致的副本。由此可见,实现热克隆的关键是本地撤销,因此热克隆必须使用 local undo。

图 1 本地 PDB 克隆

图 2 远程 PDB 克隆

图 3 远程 non-cdb 克隆

四、常见应用场景

1、本地克隆

1)通过 seed 模板克隆

此方式主要应用于使用 seed 模板创建一个全新的 PDB。

① 查看 pdb 的状态

复制代码

SYS@ora19c>show pdbs;

 CON_ID     CON_NAME         OPEN MODE      RESTRICTED
---------- ---------------- --------------  ----------
   2         PDB$SEED         READ ONLY      NO

② 查看 seed 模板的 datafile

复制代码

SYS@ora19c>select con_id,name from v$datafile where con_id=2;

  CON_ID      NAME
----------   ------------------------------------------------------------
    2         /u01/app/oracle/oradata/ORA19C/pdbseed/system01.dbf
    2         /u01/app/oracle/oradata/ORA19C/pdbseed/sysaux01.dbf
    2         /u01/app/oracle/oradata/ORA19C/pdbseed/undotbs01.dbf

③ 利用 seed 模板进行新 PDB 的克隆,无需对源库执行任何操作,指定数据文件转换目录映射

复制代码

SYS@ora19c>CREATE PLUGGABLE DATABASE pdb1 ADMIN USER pdb_mgr1 IDENTIFIED BY oracle roles=(dba) file_name_convert=('/u01/app/oracle/oradata/ORA19C/pdbseed','/u01/app/oracle/oradata/ORA19C/pdb1');

Pluggable database created.

④ 打开新的 PDB 进行验证

复制代码

SYS@ora19c>show pdbs;

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDB1                           MOUNTED

SYS@ora19c>alter pluggable database pdb1 open;

Pluggable database altered.

SYS@ora19c>show pdbs;

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDB1                           READ WRITE NO

SYS@ora19c>select con_id,name from v$datafile where con_id=3;

CON_ID   NAME
----------   -----------------------------------------------------------------
3   /u01/app/oracle/oradata/ORA19C/pdb1/system01.dbf
3   /u01/app/oracle/oradata/ORA19C/pdb1/sysaux01.dbf
3   /u01/app/oracle/oradata/ORA19C/pdb1/undotbs01.dbf

2)克隆一个已存在的 PDB

此方式常用于将已存在的 PDB 快速的在本地创建镜像,拥有与源 PDB 完全相同的数据、结构、用户、权限等。

① 将刚创建的 PDB1 创建一个 u1 用户并授权,验证克隆是否会克隆用户及权限

复制代码

SYS@ora19c>alter session set container=pdb1;
Session altered.

SYS@ora19c>create user u1 identified by oracle;

User created.

SYS@ora19c>grant connect,resource to u1;

Grant succeeded.

② 通过已存在的 PDB1 克隆出 PDB2,源库可以在 read write 模式下直接进行操作

复制代码

SYS@ora19c>show pdbs;

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDB1                           READ WRITE NO

SYS@ora19c>create pluggable database pdb2 from pdb1 file_name_convert=('pdb1','pdb2');

Pluggable database created.

SYS@ora19c>show pdbs;

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDB1                           READ WRITE NO
         4 PDB2                           MOUNTED

③ 打开新创建的 PDB 进行验证

复制代码

SYS@ora19c>alter pluggable database pdb2 open;

Pluggable database altered.

SYS@ora19c>select con_id,name from v$datafile where con_id=4;

    CON_ID    NAME
----------   -----------------------------------------------------------------
         4   /u01/app/oracle/oradata/ORA19C/pdb2/system01.dbf
         4   /u01/app/oracle/oradata/ORA19C/pdb2/sysaux01.dbf
         4   /u01/app/oracle/oradata/ORA19C/pdb2/undotbs01.dbf

④ 验证克隆的新库是否存在源库的用户及权限

复制代码

SYS@ora19c>conn u1/oracle@192.168.8.101/pdb2
Connected.
U1@192.168.8.101/pdb2>select * from session_privs;

PRIVILEGE
----------------------------------------
SET CONTAINER
CREATE INDEXTYPE
CREATE OPERATOR
CREATE TYPE
CREATE TRIGGER
CREATE PROCEDURE
CREATE SEQUENCE
CREATE CLUSTER
CREATE TABLE
CREATE SESSION

U1@192.168.8.101/pdb2>select * from session_roles;

ROLE
--------------------------------------------------------
CONNECT
RESOURCE
SODA_APP

2、远程克隆

1)克隆远程已存在的 PDB

此方式常用于将已存在的 PDB 快速的在异机之间创建镜像,拥有与源 PDB 完全相同的数据、结构、用户、权限等。

①源库 pdb_mgr1 用户授 create pluggable database 权限

复制代码

SYS@ora19c>alter session set container=pdb1;

Session altered.

SYS@ora19c>grant create pluggable database to pdb_mgr1;

Grant succeeded.

② 目标 CDB 中创建 db link

复制代码

SYS@ora19c>create public database link lk_pdb1 connect to pdb_mgr1 identified by oracle using '(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.8.101)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdb1)))';

Database link created.

③ 执行远程克隆操作,源库无须进行其它操作,可以在 read write 下操作

复制代码

SYS@ora19c>CREATE PLUGGABLE DATABASE pdb1_r FROM pdb1@lk_pdb1 file_name_convert=('pdb1','pdb1_r');

Pluggable database created.

④ 打开新创建的 PDB 进行验证

复制代码

SYS@ora19c>alter pluggable database pdb1_r open;
Pluggable database altered.

SYS@ora19c>show pdbs;

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDB1_R                         READ WRITE NO

SYS@ora19c>select con_id,name from v$datafile where con_id=3;

    CON_ID   NAME
----------   --------------------------------------------------------------
         3   /u01/app/oracle/oradata/ORA19C/pdb1_r/system01.dbf
         3   /u01/app/oracle/oradata/ORA19C/pdb1_r/sysaux01.dbf
         3   /u01/app/oracle/oradata/ORA19C/pdb1_r/undotbs01.dbf

2)远程克隆 Non-CDB

此方式常用于 Non-CDB 异机迁移 CDB 生成新的 PDB。

① 查看源库的状态

复制代码

SYS@noncdb>select name,cdb,con_id from v$database; 
NAME                        CDB           CON_ID
--------------------------- --------- ----------
NONCDB                      NO                 0

② 源库 pdb_mgr1 用户授 create pluggable database 权限

复制代码

SYS@noncdb>grant create pluggable database to system;

Grant succeeded.

③ 目标 CDB 中创建 db link

复制代码

SYS@ora19c>create public database link lk_noncdb connect to system identified by oracle using '(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.8.101)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=noncdb)))';

Database link created.

SYS@ora19c>select name,cdb,con_id from v$database@lk_noncdb;

NAME                        CDB           CON_ID
--------------------------- --------- ----------
NONCDB                      NO                 0

④ 执行 noncdb 的远程克隆

复制代码

SYS@ora19c>CREATE PLUGGABLE DATABASE noncdb_pdb FROM noncdb@lk_noncdb file_name_convert=('/u01/app/oracle/oradata/NONCDB','/u01/app/oracle/oradata/ORA19C/noncdb_pdb');

Pluggable database created.

⑤ 打开新的 PDB 进行验证

复制代码

SYS@ora19c>show pdbs;

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDB1_R                         READ WRITE NO
         5 NONCDB_PDB                     MOUNTED

SYS@ora19c>alter pluggable database  NONCDB_PDB open;

Warning: PDB altered with errors.

⑥ open 失败,执行 nocdb to pdb 的脚本

复制代码

SYS@ora19c>alter session set container=NONCDB_PDB;

Session altered.

SYS@ora19c>@?/rdbms/admin/noncdb_to_pdb.sql

⑦ 打开新创建的 PDB 进行验证

复制代码

SYS@ora19c>alter pluggable database NONCDB_PDB open;

Pluggable database altered.

SYS@ora19c>show pdbs;

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDB1_R                         READ WRITE NO
         5 NONCDB_PDB                     READ WRITE NO
SYS@ora19c>select con_id,name from v$datafile where con_id=5;

    CON_ID    NAME
----------    ---------------------------------------------------------------------
         5    /u01/app/oracle/oradata/ORA19C/noncdb_pdb/system01.dbf
         5    /u01/app/oracle/oradata/ORA19C/noncdb_pdb/sysaux01.dbf
         5    /u01/app/oracle/oradata/ORA19C/noncdb_pdb/undotbs01.dbf
         5    /u01/app/oracle/oradata/ORA19C/noncdb_pdb/users01.dbf

五、特殊应用场景

1、子集克隆

从 12.1.0.2 开始,引入了 User Tablespaces,简单的说就是可以按表空间(用户创建的)来克隆 PDB。比如,当前 PDB1 中,用户新建了两个表空间 ts1,ts2,克隆只需要 ts1 表空间中的数据,那么我们可以用 USER_TABLESPACES 子句只克隆 PDB1 中的 ts1 表空间,这样大大的缩短了时间和不必要的空间开销。对于拆分数据也很有用,可以把一个库按照表空间拆分。

语法:

  • USER_TABLESPACES=ALL 默认,所有表空间都克隆;
  • USER_TABLESPACES=NONE 所有用户创建的表空间都不克隆;
  • USER_TABLESPACES=(‘ts1’) 指定只克隆 ts1;
  • USER_TABLESPACES=ALL EXCEPT(‘ts1’) 除了 ts1 之外,其他表空间都克隆。

1)源库创建表空间 ts1,ts2

复制代码

SYS@ora19c>create tablespace ts1 datafile '/u01/app/oracle/oradata/ORA19C/pdb1/ts1.dbf' size 10m;

Tablespace created.

SYS@ora19c>create tablespace ts2 datafile '/u01/app/oracle/oradata/ORA19C/pdb1/ts2.dbf' size 10m;

Tablespace created.

2)进行子集克隆,只克隆 ts1 表空间

复制代码

SYS@ora19c>CREATE PLUGGABLE DATABASE pdb1_z FROM pdb1 file_name_convert=('pdb1','pdb1_z') user_tablespaces=('ts1');

Pluggable database created.

3)打开新创建的 PDB 进行验证

复制代码

SYS@ora19c>alter pluggable database pdb1_z open;

Pluggable database altered.

SYS@ora19c>show pdbs;

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDB1                           READ WRITE NO
         4 PDB2                           READ WRITE NO
         6 PDB1_Z                         READ WRITE NO

SYS@ora19c>select con_id,name from v$datafile where con_id=6;

    CON_ID    NAME
----------    ------------------------------------------------------------
         6    /u01/app/oracle/oradata/ORA19C/pdb1_z/system01.dbf
         6    /u01/app/oracle/oradata/ORA19C/pdb1_z/sysaux01.dbf
         6    /u01/app/oracle/oradata/ORA19C/pdb1_z/undotbs01.dbf
         6    /u01/app/oracle/oradata/ORA19C/pdb1_z/ts1.dbf

仅元数据的子集克隆,使用 no data,创建语法:

复制代码

create pluggable database pdb_nodata from pdb1 file_name_convert=('pdb1','pdb1_nodata') no data;

2、利用可刷新 PDB 的功能进行数据迁移

可刷新 PDB 功能是建立在热克隆的基础之上的。

当生产 PDB 数据量非常大,需要在很短的窗口时间进行数据迁移,当有了可刷新 PDB 和热克隆的功能后,一切将变得简单。无需考虑克隆需要花多长时间,因为源数据库无需停机。当目标 PDB 变得陈旧时,我们可以对其刷新,应用自上次刷新以来积累的所有增量。即使源数据库非常庞大,增量重做通常也将小得多。最后只在需要做割接时将源 PDB 置为 read only 后进行一次增量刷新。

刷新 PDB 须注意以下几点:

  • 源库必须开启归档日志和 local undo;
  • 可以手动刷新或者自动定时刷新,但刷新时目标端必须是 mounted 状态;
  • 在不刷新期间,目标端可以以只读模式打开;
  • 如果需以读写模式打开目标端,则必须将 refresh mode 设置为 none,设置 none 之后就无法再回退回其它刷新模式;
  • 刷新 PDB 必须使用 dblink,dblink 可以指向同一个 CDB,也可以指向不同 CDB。

1)在目标 PDB 创建 db link

复制代码

SYS@ora19c>create public database link lk_pdb1 connect to pdb_mgr1 identified by oracle using '(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.8.101)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdb1)))';

Database link created.

2)通过 db link 创建 refresh PDB

复制代码

SYS@ora19c>CREATE PLUGGABLE DATABASE pdb1_ref FROM pdb1@lk_pdb1 file_name_convert=('pdb1','pdb1_ref') REFRESH MODE EVERY 60 MINUTES;

Pluggable database created.

SYS@ora19c>show pdbs;

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDB1_R                         READ WRITE NO
         4 PDB1_REF                       MOUNTED
         5 NONCDB_PDB                     READ WRITE NO

3)当 PDB 处于 REFRESH 模式时只能有 mounted 和 read only 两种状态

复制代码

SYS@ora19c>alter pluggable database pdb1_ref open;
alter pluggable database pdb1_ref open
*
ERROR at line 1:
ORA-65341: cannot open pluggable database in read/write mode

SYS@ora19c>alter pluggable database pdb1_ref open read only;

Pluggable database altered.

SYS@ora19c>select pdb_id,pdb_name,refresh_mode from cdb_pdbs;

    PDB_ID PDB_NAME        REFRES
---------- --------------- ------
         2 PDB$SEED        NONE
         4 PDB1_REF        AUTO
         5 NONCDB_PDB      NONE
         3 PDB1_R          NONE

4)PDB 只能在 mounted 状态下使用 REFRESH 功能

复制代码

SYS@ora19c>alter pluggable database refresh;   
alter pluggable database refresh
*
ERROR at line 1:
ORA-65025: Pluggable database PDB1_REF is not closed on all instances.

Alert log:
PDB1_REF(4):PDB1_REF(4):ERROR:PDB needs to be closed for auto refresh
PDB1_REF(4):Completed: alter pluggable database refresh

5)源 PDB 创建测试数据

复制代码

U1@192.168.8.101/pdb1>create table t1 as select * from dba_objects;

Table created.

U1@192.168.8.101/pdb1>select count(*) from t1;

  COUNT(*)
----------
     72359

6)模拟应用侧停应用,将源端 PDB 置为 read only

复制代码

SYS@ora19c>alter pluggable database pdb1 close immediate;

Pluggable database altered.

SYS@ora19c>alter pluggable database pdb1 open read only;

Pluggable database altered.

7)目标端手动刷新,应用最近的增量,观察目志是否正常

复制代码

SYS@ora19c>alter pluggable database pdb1_ref refresh;

Pluggable database altered.

Alert log:
2020-02-19T13:23:44.457060+08:00
alter pluggable database pdb1_ref refresh
2020-02-19T13:23:45.940479+08:00
Applying media recovery for pdb-4099 from SCN 2793352 to SCN 2793357
Remote log information: count-1
thr-1, seq-12, logfile-/u01/app/oracle/product/db_1/dbs/archparlog_1_12_4aa635f6_1029786031.arc, los-2752894, nxs-18446744073709551615
PDB1_REF(4):Media Recovery Start
2020-02-19T13:23:45.942469+08:00
PDB1_REF(4):Serial Media Recovery started
PDB1_REF(4):max_pdb is 9
2020-02-19T13:23:45.996021+08:00
PDB1_REF(4):Media Recovery Log /u01/app/oracle/product/db_1/dbs/archparlog_1_12_4aa635f6_1029786031.arc
2020-02-19T13:23:46.257650+08:00
PDB1_REF(4):Incomplete Recovery applied until change 2793357 time 02/19/2020 13:23:09
2020-02-19T13:23:46.264473+08:00
PDB1_REF(4):Media Recovery Complete (ora19c)
Completed: alter pluggable database pdb1_ref refresh

8)目标端 PDB 关闭刷新模式

复制代码

SYS@ora19c>ALTER PLUGGABLE DATABASE pdb1_ref REFRESH MODE NONE;

Pluggable database altered

9)拉起目标端 PDB

复制代码

SYS@ora19c>ALTER PLUGGABLE DATABASE pdb1_ref open read write;

Pluggable database altered.

SYS@ora19c>show pdbs;

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDB1_R                         MOUNTED
         4 PDB1_REF                       READ WRITE NO
         5 NONCDB_PDB                     MOUNTED

10)应用连接新 PDB,校验业务

复制代码

SYS@ora19c>conn u1/oracle@192.168.8.102/pdb1_ref
Connected.

U1@192.168.8.102/pdb1_ref>select count(*) from t1;

  COUNT(*)
----------
     72359

六、热克隆中常见的错误

  • 错误一:

    复制代码

    ORA-65040: operation not allowed from within a pluggable database

    解决方法: alter session set container=cdb$root;

  • 错误二:

    复制代码

    ORA-17628: Oracle error 1031 returned by remote Oracle server
    ORA-01031: insufficient privileges

    解决方法: 到源库里对用户授 create pluggable database 权限即可。

  • 错误三:

    复制代码

    ORA-19504: failed to create file '/u01/app/oracle/oradata/ORA19C/pdb1'
    ORA-27038: created file already exists

    解决方法: 文件映射路径问题,将“文件夹—文件夹”或“文件—文件”进行一一对应。

  • 错误四:

    复制代码

    ORA-65005: missing or invalid file name pattern for file-/u01/app/oracle/oradata/ORA19C/pdb1/system01.dbf

    解决方法: 路径错误或注意路径中的大小写。

  • 错误五:

    复制代码

    ORA-01578: ORACLE data block corrupted (file # 72, block # 33609)
    ORA-01110: data file 72: '/u01/app/oracle/oradata/ORA19C/pdb1_ref/system01.dbf'
    ORA-26040: Data block was loaded using the NOLOGGING option

    解决方法: 创建可刷新 PDB 时,源端未开启归档模式。

七、小结

热克隆的方式目前都已经比较成熟,并且可以灵活使用,适合多种应用场景。既可以应用于快速创建生产环境的完整副本或子集副本,也可以应用于较短停机时间的迁移。业务中断时间短,甚至无需业务中断,操作简单,不易出错,但某些场景下对环境要求较高。

作者介绍:

王毅斌, 新炬网络数据库专家。精通 Oracle、MySQL 等数据库运维技术,拥有 Oracle OCM、MySQL OCP 等认证,具有丰富的系统架构设计、数据迁移等经验,擅长 Oracle SQL 优化,参与多个电信行业核心系统的优化。

原文链接:

https://mp.weixin.qq.com/s?__biz=MzI4NTA1MDEwNg==&mid=2650791392&idx=1&sn=b44a752c58c8818de6577b5c9ea1fd67&chksm=f3f96875c48ee163af6813d4d509ee8bfc313fbdf000c58efda3766fb241a6e66be4f71c8b05&scene=27#wechat_redirect