Oracle 12c新特性-多租户的维护管理_python

云和恩墨技术专家

现就职于云和恩墨,为某省电信公司数据库运维服务;在IT行业拥有10年以上的工作经历。擅长 ORACLE 数据库运维管理、shell 脚本开发;长期服务于电信、金融,政府行业;具有丰富的数据库经验。

本文整理自上周四晚云和恩墨大讲堂分享的关于主题:Oracle 12c新特性-多租户的维护管理。

概述

Oracle 12cRelease 1自从2013年发布以来已经有3年多时间了,新的Release版本Oracle 12c R2的发布时间也在翘首以盼。在已发布的12c中有增加了很多新特性,包括多租户、存储优化(heat map,ADO)、内存组件(In-Memory)等等。目前已经有用户开始尝鲜使用12c中的新功能,今天和大家介绍和分享12c中多租户的特点及多租户的维护管理方法。

1多租户的结构和相关的概念

简单来说,12c中的多租户架构是一个容器数据库(multitenant Container DataBase ,CDB)中包含多个可插拔数据库(PluggableDataBases)。在传统的数据库架构中Oracle实例与数据库的关系是1对1(单实例环境)和1对N(RAC数据库),而在多租户的环境下,实例与数据库的关系为N对1(单实例CDB环境)和N对N(RAC多租户CDB环境)。

下面是多租户的逻辑和物理结构:

Oracle 12c新特性-多租户的维护管理_数据库_02

从逻辑结构来看,以上图为例,一个CDB中包含有一个根容器(CDB$ROOT),一个种子容器(PDB$SEED),两个PDB分别是hrpdb和salepdb。

根容器(root container ,CDB$ROOT):CDB ROOT中主要保存一些元数据和公共对象。在容器数据库中,PDB有自己独立的SYSTEM和SYSAUX表空间保存自己数据库中的元数据信息。

种子容器(seed pdb,PDB$SEED):是系统提供用来创建PDB的模板。无法在seed容器中添加或修改对象。在CDB启动之后,SEED PDB的状态是READ ONLY的状态。

PDB(Pluggabledatabases):一个CDB中可以包含0~N个PDB,PDB是提供应用服务的数据库,在上图中包含salespdb和hrpdb两个PDB,正常情况下是由salespdb和hrpdb对外提供服务,CDB$ROOT只是用来对数据库进行管理,与Unix/Linux系统中的root用户类似。新建的CDB中不包含PDB,需要单独去创建。

从物理结构来看,在一个CDB容器中,包含有:

  • 共用的控制文件;
  • 共用的redo日志文件;
  • 一个或多个临时表空间(默认情况下,CDB和PDB共用CDB$ROOT中的临时表空间,但PDB也可以创建自己的临时表空间);
  • 共用一个UNDO表空间;
  • 各自独立的SYSTEM和SYSAUX表空间(即每个PDB会有各自SYSTEM和SYSAUX表空间);
  • PDB各自应用表空间数据文件。

2创建和删除PDB

PDB的创建有下面几种方法:

  • 从PDB$SEED中创建PDB;
  • 通过克隆一个现有的PDB数据库或非CDB数据库(non-CDB)来创建一个新的PDB;
  • 使用XML元数据描述文件将数据库插入到CDB中;
  • 使用DBMS_PDB方式;

a.通过seed新建一个PDB数据库

创建一个名字为salespdb的PDB,其应用用户默认表空间为sales,数据文件保存路径在/disk1/oracle/dbs/salespdb。创建的SQL语句如下:

CREATE PLUGGABLE DATABASE salespdb ADMIN USERsalesadm IDENTIFIED BY password

  STORAGE(MAXSIZE 2G MAX_SHARED_TEMP_SIZE 100M)

  DEFAULT TABLESPACEsales

    DATAFILE'/disk1/oracle/dbs/salespdb/sales01.dbf' SIZE 250M AUTOEXTEND ON

  PATH_PREFIX= '/disk1/oracle/dbs/salespdb/'

 FILE_NAME_CONVERT = ('/disk1/oracle/dbs/pdbseed/','/disk1/oracle/dbs/salespdb/');

在上面的创建语句中,通过STORAGE子句中的MAXSIZE和MAX_SHARED_TEMP_SIZE来限制PDB可以分配空间大小和从CDB中分配的临时表空间大小。我们可以指定大小,也可以设置为UNLIMITED.

通过PATH_PREFIX和FILE_NAME_CONVERT子句来指定数据文件的名称。

也可以通过下面的命令来创建:

CREATE PLUGGABLE DATABASE salespdb ADMIN USERsalesadm IDENTIFIED BY password FILE_NAME_CONVERT = ('/disk1/oracle/dbs/pdbseed/','/disk1/oracle/dbs/salespdb/');

b.通过克隆一个现有的PDB数据库或非CDB数据库(non-CDB)来创建一个新的PDB

通过现有的pdb1创建新的PDB pdb2:

CREATE PLUGGABLE DATABASE pdb2 FROM pdb1

 FILE_NAME_CONVERT = ('/disk1/oracle/pdb1/', '/disk2/oracle/pdb2/');

通过non-cdb创建pdb

CREATE PLUGGABLE DATABASE pdb2 FROM pdb1

(1)首先需要将non-cdb置为只读

(2)创建连接指向non-cdb的dblink

(3)创建pdb

 CREATEPLUGGABLE DATABASE pdb2 FROM mydb@mydb_link FILE_NAME_CONVERT =('/disk1/oracle/non-cdb1/', '/disk2/oracle/pdb2/‘);

  CREATEPLUGGABLE DATABASE pdb2 FROM NON$CDB@mydb_link FILE_NAME_CONVERT =('/disk1/oracle/non-cdb1/', '/disk2/oracle/pdb2/‘);

 上面这两个语句是等效的。

(4)在pdb上运行@$ORACLE_HOME/rdbms/admin/noncdb_to_pdb.sql脚本

c.使用XML元数据描述文件将数据库插入到CDB中

这个创建PDB的方式其实等于是使用XML描述文件对PDB做迁移。即使用XML描述文件的方式将PDB从一个CDB迁移到另一个CDB中。在网络不通的情况下可以使用,如果网络能正常连接的情况下,可以参考使用dblink进行迁移。

具体的创建过程如下:

首先生成XML描述文件

exec dbms_pdb.DESCRIBE('/home/oracle/pdbenmo3_describe.xml','PDBENMO3');

 使用keepdatafile的方式drop掉 PDB

drop pluggable database PDBENMO3 keep datafiles;

 在数据库中验证XML描述文件是否可以插入到目标的CDB环境中

DECLARE

   compatible CONSTANT VARCHAR2(3) :=

CASE DBMS_PDB.CHECK_PLUG_COMPATIBILITY(

pdb_descr_file => '/home/oracle/pdbenmo3_describe.xml',

pdb_name      => 'PDBENMO3')

WHEN TRUE THEN 'YES'

ELSE 'NO'

END;

BEGIN

 DBMS_OUTPUT.PUT_LINE(compatible);

END;

将PDBPROD3中的数据文件目录改名mv为PDBTEST01(数据文件中存在PDBENMO3也改为PDBTEST01);

将数据库改名插入到CDB中,使用NOCOPY的方式:

CREATE PLUGGABLE DATABASE PDBTEST01 USING'/home/oracle/pdbenmo3_describe.xml'

  SOURCE_FILE_NAME_CONVERT= ('PDBENMO3', 'PDBTEST01')

NOCOPY;

d.使用DBMS_PDB方式

通常来说,我们将Non-CDB数据库迁移到PDB有下面几种情况和方法:

(1)通过克隆的方式进行迁移,即第二种方式中使用dblink方式进行创建,这种方式需要PDB和Non-CDB数据库的版本都要高于12.1.0.2,如果没有12.1.0.2,则需要将数据库升级到12.1.0.2之后的版本

(2)通过DBMS_PDB的方式生成XML元数据文件进行迁移。这种方式要求Non-CDB数据库的版本高于12c。该节我们主要介绍这种方法

(3)使用数据泵EXPDP/IMPDP的方式。这种方式要求Non-CDB数据库的版本高于11.2.0.3,在使用该方式的时候,expdp数据时,需要添加VERSION参数version=12。如果Non-CDB数据库版本低于11.2.0.3,则可以使用传输表空间的方式进行迁移

(4)使用GoldenGate或类似于Goldengate的工具进行迁移

我们看一下使用DBMS_PDB的方式将Non-CDB转化为PDB对方式,步骤如下:

(1)将Non-CDB数据库置为只读

(2)生成XML描述文件

exec dbms_pdb.describe('/home/oracle/prod4_describe.xml');

(3)关闭Non-CDB数据库

(4)在CDB数据库中使用XML描述文件创建PDB。

CREATE PLUGGABLE DATABASE ncdb USING'/home/oracle/prod4_describe.xml' COPY FILE_NAME_CONVERT =('/u01/app/oracle/oradata/PROD4','/u01/app/oracle/oradata/PRODCDB/ncdb');

创建完成之后不要立即打开PDB,还需要执行noncdb_to_pdb.sql脚本

(5)执行noncdb_to_pdb.sql脚本

alter session set container=NCDB;    

--NCDB为新建的PDB名称

@?/rdbms/admin/noncdb_to_pdb.sql

(6) 确认没有报错后,打开数据库

 alter pluggable database NCDB open;

对于PDB的删除,有两种情况:保留数据文件和不保留数据文件

对于不保留数据文件的情况,直接使用drop pluggable database including datafiles即可

drop pluggable database pdb_name includingdatafiles;

对于需要保留数据文件的情况,则在删除前需要生成描述文件将数据库unplug,然后再进行删除:

alter pluggable database PDBENMO2 unplug into'/home/oracle/pdbenmo2_describe.xml';

drop pluggable database PDBENMO2 keep datafiles;

以上是对多租户数据库的创建和删除的维护操作,接下来我们分享一下CDB和PDB的启动停止维护内容

3多租户数据库的管理

CDB的启动和停止

CDB实例的启动和停止与传统的Non-CDB数据库启动一样,同样经历nomount,mount,open几个阶段,在各个阶段中读取的数据库文件也相同。默认情况下,当CDB启动之后,CDB$ROOT是自动打开的,而CDB$SEED无法打开,其状态为READ ONLY状态。其余PDB的状态默认情况下也都是MOUNT,如果需要设置PDB随CDB启动而打开,简单设置一下即可。

在12.1.0.1版本中,如果需要PDB随CDB启动而打开,需要通过创建触发器来实现。

CREATEOR REPLACE TRIGGER open_pdbs

AFTER STARTUP ON DATABASE

BEGIN

EXECUTE IMMEDIATE 'ALTER PLUGGABLE DATABASE ALLOPEN';

END open_pdbs;

/

在12.1.0.2版本中,可以保存PDB的状态,当下次CDB重启的时候,会将PDB启动到保存的状态下。因此我们可以将PDB的状态保存为open,当下次CDB启动之后,直接打开PDB。

其实现方法如下

alter pluggable database pdbenmo3 save state;

保存的状态通过dba_pdb_saved_states视图查看

删除保存的状态:

alter pluggable database pdbenmo3 discard state;

PDB的打开和关闭

打开PDB时,需要以SYSOPER或SYSDBA身份连接到CDB$ROOT,然后发出ALTERPLUGGABLE DATABASE OPEN语句,可以指定一个或多个PDB来打开。

alter pluggable database pdbenmo3 open;

alter pluggable database all open;

alter pluggable database all except pdbenmo3 open;

使用show pdb 或查询v$pdbs视图查看打开状态

也可以使用sysdba身份连接到PDB中打开PDB。这种情况下,不必命名要打开的PDB。

启动和关闭PDB:

Oracle 12c新特性-多租户的维护管理_数据库_03


Oracle 12c新特性-多租户的维护管理_python_04

关闭PDB时,以SYSOPER或SYSDBA身份连接到CDB$ROOT,然后发出alterpluggable database close,制定一个或多个PDB名称进行关闭

Oracle 12c新特性-多租户的维护管理_大数据_05


Oracle 12c新特性-多租户的维护管理_运维_06

表空间管理

在多租户数据库环境中,对数据库表空间的管理与传统的非多租户数据库管理基本相同。无论是对用户表空间还是临时表空间,唯一需要注意的是,在对表空间数据文件进行维护的时候,需要确认对象是属于哪个PDB或者是属于CDB$ROOT。

查看当前PDB的方法:

SQL> show pdbs

    CON_IDCON_NAME              OPEN MODE RESTRICTED

---------- ---------------------------------------- ----------

   2 PDB$SEED               READ ONLY NO

   3 PDBENMO1               MOUNTED

   4 PDBENMO2               READ WRITE NO

SQL> show pdbs

    CON_IDCON_NAME              OPEN MODE RESTRICTED

---------- ---------------------------------------- ----------

   2 PDB$SEED               READ ONLY NO

   3 PDBENMO1               MOUNTED

   4 PDBENMO2               READ WRITE NO

或者查看dba_pdbs视图

切换PDB

alter session set container=pdb_name; —切换到某个PDB

alter session set container=cdb$root; —切换到cdb root

或者可以使用sqlplus或conn的方式直接连到相应的pdb数据库中 

用户管理

相比Oracle 11g之前版本中的用户管理,多租户数据库中数据库的管理有两种:本地用户和公共用户。本地用户即为某个PDB中的数据库用户,是仅存在某个PDB中的数据库用户,其管理方法与传统的非CDB环境相同。相比本地用户,公共用户是在多个PDB中具有相同用户名和验证身份的用户。

公共用户的名称不能与所有PDB中任何本地用户名称相同。公共用户是在根和每个现有和将来的PDB中具有相同身份的数据库用户。

本地用户的用户信息保存在属主PDB中的SYSTEM表空间中,而公共用户的用户信息在CDB$ROOT和各个PDB中的SYSTEM表空间中都有保存。

公共用户创建需要在CDB$ROOT根容器下创建:

SQL> alter session set container=cdb$root;

Session altered.

common_user_prefix参数设定要求公共参数需要用C##开头,可以调整修改

SQL> show parameter common_user_prefix

NAME                     TYPE VALUE

------------------------------------ -----------------------------------------

common_user_prefix             string    C##

创建公共用户

SQL> create user C##user1 identified by oracle

User created.

查看公共用户,分别在CDB$ROOT和各个PDB中存在:

SQL> select username,con_id from cdb_users whereusername like '%USER1%';

 USERNAME        CON_ID

---------------- ----------

C##USER1          1

C##USER1          3

C##USER1          4

管理角色和权限

与公共用户和本地用户类似,在创建角色role的时候,也会有公共角色和本地角色的区别,当在根容器中创建角色时,该角色为公共角色,而在具体某一个PDB中创建角色时,该角色为本地角色。

公共角色可以授予公共用户,也可以授予本地用户。同样,在某一个PDB中,可以给一个公共用户授予公共角色,也可以给它赋予本地角色。所以,公共用户在不同的PDB中的权限可能是不同的。

可以在根容器CDB$ROOT下给所有的公共用户授予公共权限,如果需要赋予一个公共权限,则在赋权SQL命令后面需要加上container=all子句。如下:

--创建公共角色role

SQL> create role c##role1;

Role created.

--当前公共用户C##USER1没有被赋予任何角色权限

SQL> select grantee,granted_role,con_id from cdb_role_privs where grantee like '%C##USER1%';

no rows selected

--给公共用户赋予公共角色role1

SQL> grant c##role1 to c##user1 container=all;

Grant succeeded.

--赋权成功

SQL> select grantee,granted_role,con_id from cdb_role_privs where grantee like '%C##USER1%';

GRANTEE       GRANTED_ROLE  CON_ID

------------------------------ ------------------------------ ----------

C##USER1       C##ROLE1       3

C##USER1       C##ROLE1       4

C##USER1       C##ROLE1       1

同样对于公共权限也一样

--当前公共用户C##USER1没有给赋予任何系统权限

SQL> select grantee,privilege,con_id from cdb_sys_privs where grantee like '%C##USER1%';

no rows selected

--用户C##USER1赋予create session权限但不加container=all子句

SQL> grant create session to C##USER1;

Grant succeeded.

--使用container=all子句给用户C##USER1赋予create table权限

SQL> grant create table to C##USER1 container=all;

Grant succeeded.

--查看用户的权限,可以看到如果使用container=all子句,则授予的是公共权限。否则是本地权限

SQL> select grantee,privilege,con_id from cdb_sys_privs where grantee like '%C##USER1%';

GRANTEE       PRIVILEGE    CON_ID

------------------------------ ---------------------------------------- ----------

C##USER1       CREATE SESSION 1

C##USER1       CREATE TABLE 1

C##USER1       CREATE TABLE 3

C##USER1       CREATE TABLE 4

在CDB多租户环境中各PDB的资源分配

对于在多租户数据库的维护中,有时候考虑到多个PDB之间可能会存在CPU,内存等资源的竞争,如何去规划和限制这些PDB的资源分配,以避免PDB之间相互影响。

在传统以前的数据库中,Oracle一直提供了资源管理器resource manager工具对各个schema之间的资源进行分配和限制。在Oracle 12c的多租户环境中,我们同样可以使用resource manager对多租户中各个PDB的资源进行规划和分配。

下面是一个规划的示例:

为CDB数据库PRODCDB中的PDBENMO1,PDBENMO2,PDBENMO3配置资源管理器,

PDBENMO1 得到5 份共享CDB 资源

PDBENMO2 得到3 份共享CDB 资源

PDBENMO3 得到2 份共享CDB 资源

没有PDB 可以得到多于80%的可用cpu 时间

没有PDB 可以得到多于30%的parallel_servers_target

DECLARE

spfileValue VARCHAR2(1000);

execText VARCHAR2(1000);

scopeValue VARCHAR2(30) := 'MEMORY';

planName VARCHAR2(100) :='DAYTIMEP';

BEGIN

dbms_resource_manager.clear_pending_area();

dbms_resource_manager.create_pending_area();

dbms_resource_manager.create_cdb_plan( plan => 'PLAN_P', comment => '');

dbms_resource_manager.create_cdb_plan_directive(

plan => 'PLAN_P',

pluggable_database => 'PDBENMO1',

comment => '',

shares => 5,

utilization_limit => 80,

parallel_server_limit => 30 );

dbms_resource_manager.create_cdb_plan_directive(

plan => 'PLAN_P',

pluggable_database => 'PDBENMO2',

comment => '',

shares => 4,

utilization_limit => 80,

parallel_server_limit => 30 );

dbms_resource_manager.create_cdb_plan_directive(

plan => 'PLAN_P',

pluggable_database => 'PDBENMO3',

comment => '',

shares => 1,

utilization_limit => 80,

parallel_server_limit => 30 );

dbms_resource_manager.submit_pending_area();

END;

/

如上,可以使用dbms_resource_manager资源管理器包对CDB中各个PDB分配的资源进行规划和限制。

4总结

  1. 部署的数据库对硬件的资源消耗并不大,系统压力比较小
  2. 具有实例和存储空间的开销,因此同一台服务器上不能同时部署很多套数据库,不同的应用分散在不同的服务器上
  3. 每一套数据库的维护不是很复杂,但是维护的数据库量比较大
  4. 不需要大量的时间来对数据库进行升级或打补丁

对于以上这种情况,我们可以考虑使用Oracle 12c多租户的功能,将很多个数据库集中到CDB中进行维护管理。

多租户数据库的优点:

(1)在集中管理的平台中操作多个数据库,其成本更低:

—实例开销较低

—存储成本较低

(2)减少DBA的重复维护工作量并保证维护的安全性

—对应用来说,数据库的连接并没有改变,不会涉及应用程序的更改

—对DBA数据库管理员来说,减少了数据库的维护量

—数据库维护更方便,CDB/PDB的维护工作与传统的维护方式没有太大改变,相比在升级和打补丁方面更方便

—新建PDB和迁移比较快速

(3)不同的PDB之间相互隔离,不会相互干扰影响