CDB多租户容器数据库可插入数据库的概念

Oracle Multitenant Container Database(CDB),即多租户容器数据库,是Oracle 12C引入的特性,指的是可以容纳一个或者多个可插拔数据库的数据库,这个特性允许在CDB容器数据库中创建并且维护多个数据库,在CDB中创建的数据库被称为PDB,每个PDB在CDB中是相互独立存在的,在单独使用PDB时,与普通数据库无任何区别。

CDB根容器数据库的主要作用就是容纳所有相关的PDB的元数据,以及在CDB中对所有的PDB进行管理。

多租户环境的组成ROOT容器数据库

Root容器数据库,是CDB环境中的根数据库,在根数据库中含有主数据字典视图,其中包含了与Root容器有关的元数据和CDB中所包含的所有的PDB信息。在CDB环境中被标识为CDB$ROOT,每个CDB环境中只能有一个Root容器数据库。

CDB seed

CDB seed为PDB的种子,其中提供了数据文件,在CDB环境中被标识为PDB$SEED,是创建新的 PDB的模板,你可以连接PDB$SEED,但是不能执行任何事物,因为PDB$SEED是只读的,不可进行修改。

PDBs

PDB数据库,在CDB环境中每个PDB都是独立存在的,与传统的Oracle数据库基本无差别,每个PDB拥有自己的数据文件和objects,唯一的区别在于PDB可以插入到CDB中,以及在CDB中拔出,并且在任何一个时间点之上PDB必须拔出或者插入到一个CDB中,当用户链接PDB时不会感觉到根容器和其他PDB的存在。

数据库CDB和PDB结构图

通过以下的命令查询当前数据库中的pdb:

SQL>showpdbsCON_IDCON_NAMEOPENMODERESTRICTED------------------------------------------------------------2PDB$SEEDREADONLYNO3ORCLPDBMOUNTEDSQL>

其中PDB$SEED为CDB seed,ORCLPDB为PDB数据库。

Application Containers

在12cR2版本中,Oracle对多租户功能进行了增强,在CDB root容器中可以创建一个叫做Application root的容器,可在其内创建多个依赖于Application root的Application PDBs,架构图如下:

12c R2 CDB和PDB结构图(新增 Application Containers)

CDB环境中的用户

CDB环境中包含两类用户,公用用户和本地用户。

公用用户创建公用用户

公用用户是在root容器数据库中和所有的PDB数据库中都存在的用户,公用用户必须在根容器中创建,然后此用户会在所有的现存的PDB中自动创建,公用用户标识必须以c##或者C##开头,sys和system用户是Oracle在CDB环境中自动创建的公用用户。

举例:

创建语句为:

SQL>createuserc##mytest1identifiedbymytest1;Usercreated.

小结:

(1) 公用用户在root容器中创建

(2) 公用用户名称比较特殊,要以c##或者C##开头

公用用户赋权

创建完成公用用户,需要为公用用户赋予所有可插拔数据库的权限,公用用户才可以访问其他PDB,如果在连接根容器时仅仅为公用用户赋予了相关权限,那么该权限不会被传递到所有的可插拔数据库中,必须为公用用户赋予能够传递到PDB中的权限,可以创建公用角色然后赋予公用用户,或者在为公共用户付权时指定子句container=ALL;

小结:

(1) 公用用户要连接pdb需要赋予对应权限

(2) 赋予的权限方式可以是加container=all(或container=pdb);也可以通过赋予role角色权限。

举例:

赋予c##mytest1用户基本的连接权限。

SQL>grantresource,connecttoc##mytest1;Grantsucceeded.SQL>SQL>exit

测试”c##mytest1”用户的登录情况,可以正常登录

[oracle@linux14~]$sqlplus/nologSQL*Plus:Release12.2.0.1.0ProductiononSatJan523:01:302019Copyright(c)1982,2016,Oracle.Allrightsreserved.SQL>connc##mytest1/mytest1;Connected.SQL>

在没有赋予的访问pdb的相关权限下,登录失败

SQL>altersessionsetcontainer=orclpdb;ERROR:ORA-01031:insufficientprivilegesSQL>

赋予c##mytest1用户pdb访问权限的2种方式

(1)在赋权的时候加 container=all

[oracle@linux14~]$sqlplus/assysdbaSQL*Plus:Release12.2.0.1.0ProductiononSatJan523:08:152019Copyright(c)1982,2016,Oracle.Allrightsreserved.Connectedto:OracleDatabase12cEnterpriseEditionRelease12.2.0.1.0-64bitProductionSQL>grantresource,connecttoc##mytest1container=all;Grantsucceeded.SQL>connc##mytest1/mytest1Connected.SQL>SQL>altersessionsetcontainer=orclpdb;Sessionaltered.SQL>(2)赋予已有的角色权限

1.创建c##mytestprivs的角色-->

2.赋予resource,connect权限-->

3.然后把c##mytestprivs赋予c##mytest1-->

4.然后c##mytest1就可以正常登录pdb

[oracle@linux14~]$sqlplus/assysdbaSQL*Plus:Release12.2.0.1.0ProductiononSatJan523:19:162019Copyright(c)1982,2016,Oracle.Allrightsreserved.Connectedto:OracleDatabase12cEnterpriseEditionRelease12.2.0.1.0-64bitProductionSQL>SQL>createrolec##mytestprivscontainer=all;Rolecreated.SQL>grantresource,connecttoc##mytestprivscontainer=all;Grantsucceeded.SQL>grantc##mytestprivstoc##mytest1container=all;Grantsucceeded.SQL>SQL>connc##mytest1/mytest1Connected.SQL>SQL>showuserUSERis"C##MYTEST1"SQL>SQL>altersessionsetcontainer=orclpdb;Sessionaltered.SQL>本地用户

本地用户指的是在PDB中创建的普通用户,只有在创建它的PDB中才会存在该用户,并且PDB中只能创建本地用户。此用户跟普通数据库中的用户一样。

12.2中关于CDB你需要了解的基础知识SYSTEM/SYSAUX表空间

SYSTEM和SYSAUX表空间是独立的,在PDB和root容器数据库中各自拥有,并非共用。

在CDB$ROOT容器数据库中查询结果:

SQL>showcon_idcon_nameuser;CON_ID------------------------------1CON_NAME------------------------------CDB$ROOTUSERis"SYS"SQL>SQL>SQL>selecttablespace_name,file_namefromdba_data_files;TABLESPACE_NAMEFILE_NAME---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------USERS/data/oradata/orcl/users01.dbfUNDOTBS1/data/oradata/orcl/undotbs01.dbfSYSTEM/data/oradata/orcl/system01.dbfSYSAUX/data/oradata/orcl/sysaux01.dbf

在orclpdb的pdb数据库中查询结果为:

SQL>altersessionsetcontainer=orclpdb;Sessionaltered.SQL>showcon_idcon_nameuser;CON_ID------------------------------3CON_NAME------------------------------ORCLPDBUSERis"SYS"SQL>SQL>selecttablespace_name,file_namefromdba_data_files;TABLESPACE_NAMEFILE_NAME---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------UNDOTBS1/data/oradata/orcl/orclpdb/undotbs01.dbfSYSAUX/data/oradata/orcl/orclpdb/sysaux01.dbfSYSTEM/data/oradata/orcl/orclpdb/system01.dbfUSERS/data/oradata/orcl/orclpdb/users01.dbfSQL>REDO文件

(1)redo日志文件为公用的,存在于root容器中CDB$ROOT,REDO中的条目标识REDO是来自哪个PDB。

(2)在PDB中无法执行ALTER SYSTEM SWITCH LOGFILE命令,只有公用用户在ROOT容器中才可以执行该命令。

SQL>altersystemswitchlogfile;altersystemswitchlogfile*ERRORatline1:ORA-65040:operationnotallowedfromwithinapluggabledatabase

(3) 另外ALTER SYSTEM CHECKPOINT命令是可以在PDB中执行的。

SQL>altersystemcheckpoint;Systemaltered.SQL>归 档

归档日志是公用的

在CDB环境中所有的PDB共用CDB的归档模式,以及归档文件,不可以单独为PDB设置自己的归档模式,只有特权用户连接根容器之后才可以启动归档模式。

UNDO MODE- -UNDO表空间

(1) 在12.2之前的版本中,所有的PDB共用CDB$ROOT中的UNDO文件

(2) 在12.2之后的版本中UNDO的使用模式有两种:SHARED UNDO MODE和LOCAL UNDO MODE,顾名思义,LOCAL UNDO MODE就是每个PDB使用自己的UNDO表空间,但当PDB中没有自己的UNDO表空间时,会使用CDB$ROOT中的公共UNDO表空间。

查看UNDO表空间的使用模式(CDB$ROOT):

SQL>COLPROPERTY_NAMEFORA50SQL>COLPROPERTY_VALUEFORA50SQL>COLDESCRIPTIONFORA50SQL>SQL>SELECTproperty_name,property_value2FROMdatabase_properties3WHEREproperty_name='LOCAL_UNDO_ENABLED';PROPERTY_NAMEPROPERTY_VALUE----------------------------------------------------------------------------------------------------LOCAL_UNDO_ENABLEDTRUESQL>关闭Local UNDO (操作需要重启数据库)

直接关闭local undo会报错:

SQL>alterdatabaselocalundooff;alterdatabaselocalundooff*ERRORatline1:ORA-65192:databasemustbeinUPGRADEmodeforthisoperation

关闭数据库,启动到upgrade模式下关闭local undo

SQL>shutdownimmediate;Databaseclosed.Databasedismounted.ORACLEinstanceshutdown.SQL>SQL>startupupgradeORACLEinstancestarted.TotalSystemGlobalArea1241513984bytesFixedSize8792248bytesVariableSize788531016bytesDatabaseBuffers436207616bytesRedoBuffers7983104bytesDatabasemounted.Databaseopened.SQL>SQL>alterdatabaselocalundooff;Databasealtered.

重启数据库后,查询local undo已经关闭了

SQL>shutdownimmediate;Databaseclosed.Databasedismounted.ORACLEinstanceshutdown.SQL>startupORACLEinstancestarted.TotalSystemGlobalArea1241513984bytesFixedSize8792248bytesVariableSize788531016bytesDatabaseBuffers436207616bytesRedoBuffers7983104bytesDatabasemounted.Databaseopened.SQL>SQL>SELECTproperty_name,property_value2FROMdatabase_properties3WHEREproperty_name='LOCAL_UNDO_ENABLED';PROPERTY_NAMEPROPERTY_VALUE----------------------------------------------------------------------------------------------------LOCAL_UNDO_ENABLEDFALSESQL>开启local UNDO(操作需要重启数据库)

直接开启local undo也会报错

SQL>alterdatabaselocalundoon;alterdatabaselocalundoon*ERRORatline1:ORA-65192:databasemustbeinUPGRADEmodeforthisoperation

重启数据库到upgrade模式,然后关闭local undo

SQL>shutdownimmediate;Databaseclosed.Databasedismounted.ORACLEinstanceshutdown.SQL>SQL>startupupgradeORACLEinstancestarted.TotalSystemGlobalArea1241513984bytesFixedSize8792248bytesVariableSize788531016bytesDatabaseBuffers436207616bytesRedoBuffers7983104bytesDatabasemounted.Databaseopened.SQL>SQL>alterdatabaselocalundoon;Databasealtered.

再次重启数据库,查询local undo的开启状态

SQL>shutdownimmediate;Databaseclosed.Databasedismounted.ORACLEinstanceshutdown.SQL>startupORACLEinstancestarted.TotalSystemGlobalArea1241513984bytesFixedSize8792248bytesVariableSize788531016bytesDatabaseBuffers436207616bytesRedoBuffers7983104bytesDatabasemounted.Databaseopened.SQL>SQL>SELECTproperty_name,property_value2FROMdatabase_properties3WHEREproperty_name='LOCAL_UNDO_ENABLED';PROPERTY_NAMEPROPERTY_VALUE----------------------------------------------------------------------------------------------------LOCAL_UNDO_ENABLEDTRUESQL>临时文件

(1)默认情况下临时表空间也是独立的,PDB和root容器数据库各自拥有,

(2)如果PDB没有自己的临时表空间文件,那么,PDB可以使用CDB$ROOT中的临时表空间。

参数文件

参数文件是公用的

(1) 参数文件中只记录了根容器的参数信息,没有记录PDB级别的参数信息,在根容器中修改初始化参数,会被继承到所有的PDB中,

(2) 在PDB中修改参数后,PDB的参数会覆盖CDB级别的参数,PDB级别的参数记录在根容器的pdb_spfile$视图中,但并不是所有的参数都可以在PDB中修改,

(3) 可以通过v$system_parameter视图查看PDB中可修改的参数:

SELECTnameFROMv$system_parameterWHEREispdb_modifiable='TRUE'ORDERBYname;控制文件

控制文件是公用的

CDB环境中只有一组控制文件,所有的PDB共用这组公共的控制文件,从任何PDB中添加数据文件都会记录到公共控制文件当中,公用用户连接根容器时,可对控制文件进行管理。

SQL>showparametercontrol_files;NAMETYPEVALUE-----------------------------------------------------------------------------control_filesstring/data/oradata/orcl/control01.ctl,/data/flash_recovery_area/orcl/control02.ctlSQL>告警日志以及跟踪文件

告警日志和跟踪文件是公用的

在CDB中所有的PDB共用一个告警日志和一组跟踪文件,所有的PDB告警信息都会写入同一个告警日志中。

时区

在CDB环境中可以为CDB以及所有的PDB设置相同的时区,也可以为每个PDB设置单独的时区。

查询时区语句:

SQL>selectdbtimezonefromdual;DBTIME------+00:00字符集

在CDB中定义字符集也可以应用于它所含有的PDB中,每个PDB也可以有自己的字符集设置。

查询字符集的语句:

SELECTa.value||'_'||b.value||'.'||c.valueNLS_LANGFROMnls_database_parametersa,nls_database_parametersb,nls_database_parameterscWHEREa.parameter='NLS_LANGUAGE'ANDb.parameter='NLS_TERRITORY'ANDc.parameter='NLS_CHARACTERSET';数据字典视图与动态性能视图

(1) 在CDB环境中引入了CDB级别的数据字典视图,它的级别高于DBA_/ALL_/USER_,CDB级别的数据字典视图含有所有PDB的元数据信息,其中增加了con_id列,con_id为CDB中所有容器唯一标识符,

(2) 其中con_id为的是CDB$ROOT,con_id为2的是PDB$SEED,每个PDB在CDB中都会分配一个唯一的con_id。

(3) 如果要想查看CDB级别的数据字典视图,必须使用公用用户在跟容器中查看,并且要查看的PDB必须处于open状态,才可以看到PDB中的信息。

CDB的操作和管理

管理CDB通常也是使用sys用户,其连接和操作方式跟非CDB数据库相同。

登录CDB和PDB数据库的方法

在oracle 12c中使用tnsname的方式来进行登录CDB和PDB

tnsnames.ora的举例配置

[oracle@linux14admin]$cattnsnames.ora#tnsnames.oraNetworkConfigurationFile:/opt/app/oracle/product/12c/db_1/network/admin/tnsnames.ora#GeneratedbyOracleconfigurationtools.LISTENER_ORCL=(ADDRESS=(PROTOCOL=TCP)(HOST=linux14)(PORT=1521))ORCL=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=linux14)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=orcl)))ORCLPDB=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=linux14)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=orclpdb)))

其中的ORCL的配置是用于登录CDB的cdb$root容器数据库

ORCLPDB的配置是用于登录PDB为orclpdb数据库

使用orcl来登录cdb$root数据库:

[oracle@linux14admin]$sqlplussys/oracle@orclassysdbaSQL*Plus:Release12.2.0.1.0ProductiononTueJan822:13:152019Copyright(c)1982,2016,Oracle.Allrightsreserved.Connectedto:OracleDatabase12cEnterpriseEditionRelease12.2.0.1.0-64bitProductionSQL>showcon_idcon_nameuser;CON_ID------------------------------1CON_NAME------------------------------CDB$ROOTUSERis"SYS"

使用orclpdb来登录pdb数据库

[oracle@linux14admin]$sqlplussys/oracle@orclpdbassysdbaSQL*Plus:Release12.2.0.1.0ProductiononTueJan822:14:372019Copyright(c)1982,2016,Oracle.Allrightsreserved.Connectedto:OracleDatabase12cEnterpriseEditionRelease12.2.0.1.0-64bitProductionSQL>showcon_idcon_nameuser;CON_ID------------------------------3CON_NAME------------------------------ORCLPDBUSERis"SYS"SQL>查询当前连接容器的信息:

(1) 方式1

SQL>showcon_idcon_nameuser;CON_ID------------------------------1CON_NAME------------------------------CDB$ROOTUSERis"SYS"SQL>

(2)方式2

SQL>COLCON_IDFORA10SQL>COLCUR_CONTAINERFORA25SQL>COLCUR_USERFORA25SQL>SELECT2sys_context('USERENV','CON_ID')con_id,3sys_context('USERENV','CON_NAME')cur_container,4sys_context('USERENV','session_user')cur_user5FROMdual;CON_IDCUR_CONTAINERCUR_USER------------------------------------------------------------1CDB$ROOTSYSSQL>启动和关闭CDB数据库

启动和关闭CDB数据库的方式跟普通的非CDB数据库一致,其中PDB的数据库不会随着CDB一起其中,只会到mounted状态:

[oracle@linux14~]$sqlplus/assysdbaSQL*Plus:Release12.2.0.1.0ProductiononTueJan820:47:242019Copyright(c)1982,2016,Oracle.Allrightsreserved.Connectedtoanidleinstance.SQL>startupORACLEinstancestarted.TotalSystemGlobalArea1241513984bytesFixedSize8792248bytesVariableSize788531016bytesDatabaseBuffers436207616bytesRedoBuffers7983104bytesDatabasemounted.Databaseopened.SQL>SQL>showpdbs;CON_IDCON_NAMEOPENMODERESTRICTED------------------------------------------------------------2PDB$SEEDREADONLYNO3ORCLPDBMOUNTEDSQL>alterpluggabledatabaseorclpdbopen;Pluggabledatabasealtered.SQL>

手工启动PDB到open

alterpluggabledatabaseorclpdbopen;-->指定某个pdb启动,比如orclpdbalterpluggabledatabaseallopen;-->所有的pdb数据库都启动

关闭CDB的命令:

SQL>shutdownimmediate;

查询CDB数据库的表空间使用情况

withgenerator0as(selectcf.con_id,cf.tablespace_name,sum(cf.bytes)/1024/1024frmfromcdb_free_spacecfgroupbycf.con_id,cf.tablespace_name),generator1as(selectcd.con_id,cd.tablespace_name,sum(cd.bytes)/1024/1024usmfromcdb_data_filescdgroupbycd.con_id,cd.tablespace_name),generator2as(selectg0.con_id,c.namecon_name,g0.tablespace_name,g0.frm,g1.usmfromgenerator0g0,generator1g1,v$containerscwhereg0.con_id=g1.con_idandg0.tablespace_name=g1.tablespace_nameandc.con_id=g1.con_idunionselectc.con_id,c.name,ct.tablespace_name,null,sum(ct.bytes)/1024/1024fromv$containersc,cdb_temp_filesctwherec.con_id=ct.con_idgroupbyc.con_id,c.name,ct.tablespace_name)selectcon_id,casewhencon_name=LAG(con_name,1)OVER(PARTITIONBYcon_nameORDERBYtablespace_name)THENnullELSEcon_nameENDcon_name,tablespace_name,frm||'M'freemb,usm||'M'usembfromgenerator2orderbycon_id;

结果如图所示:

切换容器的操作

正常情况下都是用公用用户来做操作

使用公用用户连接CDB后,可以使用alter session的方式切换不同的容器。

例如:

altersessionsetcontainer=orclpdb;altersessionsetcontainer=cdb$root;实验:

在pdb数据库orclpdb中创建了mytest1用户,用此用户进行切换容器,会报没有权限的错误

[oracle@linux14admin]$sqlplusmytest1/mytest1@orclpdb;SQL*Plus:Release12.2.0.1.0ProductiononTueJan822:01:092019Copyright(c)1982,2016,Oracle.Allrightsreserved.LastSuccessfullogintime:TueJan08201921:50:33+08:00Connectedto:OracleDatabase12cEnterpriseEditionRelease12.2.0.1.0-64bitProductionSQL>SQL>altersessionsetcontainer=cdb$root;ERROR:ORA-01031:insufficientprivileges

尝试用sys用户给mytest1用户赋予权限,也出现了报错

SQL>connsys/oracleassysdbaConnected.SQL>showcon_iduser;CON_ID------------------------------1USERis"SYS"SQL>SQL>SQL>altersessionsetcontainer=orclpdb;Sessionaltered.SQL>SQL>showcon_iduser;CON_ID------------------------------3USERis"SYS"SQL>SQL>SQL>grantdbatomytest1container=all;grantdbatomytest1container=all*ERRORatline1:ORA-65030:cannotgrantaprivilegecommonlytoalocaluserorroleSQL>

因此建议,CDB容器数据库的操作使用公用用户来进行相关的操作。