网站首页 > 技术文章 正文
原文链接 https://blog.dbi-services.com/oracle-12c-cdb-metadata-a-object-links-internals/
译者 周天鹏
温馨提示:这篇文章只适合那些想了解多租户环境下数据字典、元数据和对象链接相关技术内幕的geek群体!对于你日常运维数据库来说并没有什么太大用处。千万别再生产环境上这么搞,你可能会损毁你的数据字典。
在12c的CDB中,我们知道每个PDB都是独立的。但这些PDB为了能整合到一个CDB里,会共享一些公共资源。例如,CPU、内存、redo和undo。他们都被实例在CDB级别进行管理。对于数据来说,共享公共资源也很简单,因为PDB有自己独立的表空间,而且,可插拔特性仅仅是可传输表空间技术的一种拓展。
对于12c的多租户架构来说,最具挑战性的技术难题是如何共享数据字典。
首先,虽然每个PDB有自己的元数据描述自己独有的信息。但是,数据字典自身的元数据必须共享,举个例子就是,所有dbms_xxx的PL/SQL包都存储在CDB$ROOT中,PDB中仅存放指向他们的一个链接。
除此之外,一些数据字典中的数据也必须被共享,例如一些引用表(AUDIT_ACTIONS)或者公共资料库(利用AWR数据构造出的DBA_HIST_xxx这种表),他们也都存储在CDB$ROOT中,每个PDB仅定义一个视图指向他们。
最后,CDB$ROOT必须有能力查询所有PDB的数据。例如通过12c新增的CDB_xxx视图。虽然他们暴露为用来查询容器数据的对象,但其实他们真正查询的数据还是存储在每个PDB中。
这听起来似乎有点迷,虽然官方文档也不会很深入的讲具体的实现原理。但幸运的是?/rdbms/admin这个脚本中有一些我们想要的线索。这里描述了当 "_ORACLE_SCRIPT"参数置为true时,SQL语法将如何进行拓展。
所以,geek们来了,让我们一起尝试下自己创建元数据和对象链接。
下面的操作需要先把我们当前会话的"_ORACLE_SCRIPT"参数置为true。
然后,我们将看到一种新的拓展SQL语法:cdb$view(), sharing=metadata, sharing=object, common_data
容器数据对象
首先让我们看下根容器如何查看其他容器的数据。
我在根容器中:
SQL> alter session set container=cdb$root;
Session altered.
SQL> show con_name
CON_NAME
------------------------------
CDB$ROOT
SQL> show con_id
CON_ID
------------------------------
1
创建一个规则表:
SQL> create table DEMO_REG_TABLE sharing=none as select 111 dummy from dual;
Table created.
SQL> select * from DEMO_REG_TABLE;
DUMMY
----------
111
然后,我在PDB中执行相同操作(但数据不同):
SQL> alter session set container=pdb1;
Session altered.
SQL> show con_name
CON_NAME
------------------------------
PDB1
SQL> show con_id
CON_ID
------------------------------
3
SQL> create table DEMO_REG_TABLE sharing=none as select 999 dummy from dual;
Table created.
SQL> select * from DEMO_REG_TABLE;
DUMMY
----------
999
这时,回到根容器,我使用CDB$VIEW函数来查看所有PDB中的信息。
SQL> select * from cdb$view(DEMO_REG_TABLE) where con_id in (1,3);
DUMMY CON_ID
---------- ----------
999 3
111 1
这就是内置容器对象的定义方式。他们用CDB$VIEW来查询每个PDB中的数据。整合后的结果加上CON_ID来表示这些数据来自哪个PDB。
想知道具体如何实现吗?目测是用了一个运行在每个PDB上的并行查询。证据如下:
先前我的查询条件是CON_ID in (1,3),因为我没有在所有PDB上创建我的表。当我不加这个where条件时,我会收到如下报错:
SQL> select * from cdb$view(DEMO_REG_TABLE);
select * from cdb$view(DEMO_REG_TABLE)
*
ERROR at line 1:
ORA-12801: error signaled in parallel query server P002
ORA-00942: table or view does not exist
并行进程的报错,这个PDB里找不到表了。
元数据链接
现在我将在根容器和PDB中创建一个函数。但是我不想让这些代码被存储两份。我会使用SHARING=METADATA来定义元数据链接。
SQL> alter session set container=cdb$root;
Session altered.
SQL> show con_name
CON_NAME
------------------------------
CDB$ROOT
SQL> show con_id
CON_ID
------------------------------
1
SQL> create function DEMO_MDL_FUNCTION sharing=metadata
2 return varchar2 as dummy varchar2(100); begin select max(dummy) into dummy from DEMO_REG_TABLE; return dummy; end;
3 /
Function created.
SQL> select DEMO_MDL_FUNCTION from dual;
DEMO_MDL_FUNCTION
------------------------------
111
这是我CDB$ROOT中的函数,它展示我的CDB$ROOT中的的一张普通表里的内容。
现在,在PDB中做同样的操作。
SQL> alter session set container=pdb1;
Session altered.
SQL> show con_name
CON_NAME
------------------------------
PDB1
SQL> show con_id
CON_ID
------------------------------
3
SQL> create function DEMO_MDL_FUNCTION sharing=metadata
2 return varchar2 as dummy varchar2(100); begin select max(dummy) into dummy from DEMO_REG_TABLE; return dummy; end;
3 /
Function created.
SQL> select DEMO_MDL_FUNCTION from dual;
DEMO_MDL_FUNCTION
------------------------------
999
我在我的PDB中有了一个同样的函数,展现PDB中的一张普通表内的数据。
我可以从SYS.SOURCE$数据字典中查出我定义的函数的元数据。如下:
SQL> alter session set container=cdb$root;
Session altered.
SQL> select * from source$ where obj# in (select obj# from obj$ where name like 'DEMO%');
OBJ# LINE SOURCE
---------- ---------- ------------------------------
95789 1 function DEMO_MDL_FUNCTION
但是,再看下我们的PDB中有啥:
SQL> alter session set container=pdb1;
Session altered.
SQL> select * from source$ where obj# in (select obj# from obj$ where name like 'DEMO%');
no rows selected
结果发现PDB中啥也没有存,只能在obj$中查到这个对象,类型是元数据链接。
但如果我再查一下dba_source,这里又有另一个迷:
SQL> select * from dba_source where name like 'DEMO%';
OWNER NAME TYPE LINE TEXT ORIGIN_CON_ID
----- ----------------- --------- ---- --------------------------- -------------
SYS DEMO_MDL_FUNCTION FUNCTION 1 function DEMO_MDL_FUNCTION 1
PDB的DBA_SOURCE中包含了CDB$ROOT中的信息,元信息字段的后面加了ORIGIN_CON_ID这个字段来表示该信息来自PDB的数据字典还是CDB$ROOT的数据字典。这里显然表示了该函数来自CDB$ROOT。(公共数据视图部分有详解)
对象链接
我们已经看到了CDB$ROOT是如何存储所有PDB的元信息的。我们将使用元数据连接来创建一张表。除此之外,我们还要创建一个对象链接,这样CDB$ROOT中的表才能存储所有PDB的信息。我用SHARING=METADATA来建表,SHARING=OBJECT来建视图。
首先,我在所有容器中建表:
SQL> alter session set container=cdb$root;
Session altered.
SQL> show con_name
CON_NAME
------------------------------
CDB$ROOT
SQL> show con_id
CON_ID
------------------------------
1
SQL> create table DEMO_MDL_TABLE sharing=metadata as select 111 dummy from dual;
Table created.
SQL> alter session set container=pdb1;
Session altered.
SQL> show con_name
CON_NAME
------------------------------
PDB1
SQL> show con_id
CON_ID
------------------------------
3
SQL> create table DEMO_MDL_TABLE sharing=metadata as select 999 dummy from dual;
Table created.
这样每个容器中就都创建了这张表。为了更好的理解发生了什么,我往这些表里插入不同的数据。接下来用CDB$VIEW查询所有容器中的数据。
SQL> alter session set container=cdb$root;
Session altered.
SQL> select * from cdb$view(DEMO_MDL_TABLE) where con_id in (1,3);
DUMMY CON_ID
---------- ----------
999 3
111 1
这是两张表结构相同的表,CDB$ROOT中的数据是111,PDB中的是999。
我要在这个表上创建一个视图,定义它是一个对象链接,这样里面的数据就可以被共享了。
SQL> alter session set container=cdb$root;
Session altered.
SQL> show con_name
CON_NAME
------------------------------
CDB$ROOT
SQL> show con_id
CON_ID
------------------------------
1
SQL> create view DEMO_OBL_VIEW sharing=object as select * from DEMO_MDL_TABLE;
View created.
SQL> select * from DEMO_OBL_VIEW;
DUMMY
----------
111
CDB$ROOT中的视图展示了CDB$ROOT中的数据,现在我们再PDB中做同样的操作。
SQL> alter session set container=pdb1;
Session altered.
SQL> show con_name
CON_NAME
------------------------------
PDB1
SQL> show con_id
CON_ID
------------------------------
3
SQL> create view DEMO_OBL_VIEW sharing=object as select * from DEMO_MDL_TABLE;
View created.
SQL> select * from DEMO_OBL_VIEW;
DUMMY
----------
111
PDB中的这个视图也展示了CDB$ROOT中的数据。这个查询用了对象链接,而不是使用当前容器中的表。
想想AWR快照,AWR快照只运行在CDB级别,然后将数据存在WRM$表中。最终每个PDB依然可以通过DBA_HIST_*视图来查看这些数据。
PS: 你无法向一个对象链接中插入数据
SQL> insert into DEMO_OBL_VIEW select 9999 dummy from dual;
insert into DEMO_OBL_VIEW select 9999 dummy from dual
*
ERROR at line 1:
ORA-02030: can only select from fixed tables/views
这里有一个关于实现方法的线索,如果你从PDB中看执行计划,你可以发现对象链接访问的是一个fixed table。
---------------------------------------------
| Id | Operation | Name |
---------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | FIXED TABLE FULL| X$OBLNK$aed0818c |
---------------------------------------------
公共数据视图
最后让我们看看PDB是如何展示来自CDB$ROOT中的数据的。 像DBA_SOURCE这种字典表,必须要展示公共元数据和PDB元数据。它被定义为公共数据视图,我这就用COMMON_DATA关键字创建一个。
SQL> alter session set container=cdb$root;
Session altered.
SQL> show con_name
CON_NAME
------------------------------
CDB$ROOT
SQL> show con_id
CON_ID
------------------------------
1
SQL> create or replace view DEMO_INT_VIEW common_data (dummy,sharing) as select dummy,case when dummy='222' then 0 else 1 end from DEMO_MDL_TABLE;
View created.
SQL> select * from DEMO_INT_VIEW;
DUMMY SHARING
---------- ----------
111 1
222 0
我增加了一个“SHARING”字段(使用COMMON_DATA关键字时必须要有)来标记那些行是共享给其他容器,那些行是不共享的。“222”那行是这个容器私有的,“111”那行可以被其他PDB看到。我在PDB中也要做相同的操作:
SQL> alter session set container=pdb1;
Session altered.
SQL> show con_name
CON_NAME
------------------------------
PDB1
SQL> show con_id
CON_ID
------------------------------
3
SQL> create or replace view DEMO_INT_VIEW common_data (dummy,sharing) as select dummy,case when dummy='222' then 0 else 1 end from DEMO_MDL_TABLE;
View created.
SQL> select * from DEMO_INT_VIEW;
DUMMY SHARING ORIGIN_CON_ID
---------- ---------- -------------
999 1 3
111 1 1
当再PDB中时,COMMON_DATA视图除了PDB中的行,还会展示CDB$ROOT中共享的行。当然,从上面读下来后,你期待着看到并行进程和fixed table:
SQL> set autotrace on
SQL> select * from DEMO_INT_VIEW;
DUMMY SHARING ORIGIN_CON_ID
---------- ---------- -------------
111 1 1
999 1 3
Execution Plan
----------------------------------------------------------
Plan hash value: 3158883863
--------------------------------------------------------------------------------------------
|Id | Operation | Name |Pstart|Pstop | TQ |IN-OUT| PQ Distrib |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | | | |
| 1 | PX COORDINATOR | | | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10000 | | | Q1,00 | P->S | QC (RAND) |
| 3 | PX PARTITION LIST ALL| | 1 | 2 | Q1,00 | PCWC | |
| 4 | FIXED TABLE FULL | X$COMVW$e40eb386| | | Q1,00 | PCWP | |
--------------------------------------------------------------------------------------------
这个fixed table把每个容器中的数据作为一个分区返回,均为并行处理。
对于多租户环境下数据字典的技术内幕,我们的探索已经足够了。
如果你还想知道更多,就看看?/rdbms/admin/noncdb_to_pdb.sql这个脚本里的内容吧,这里有你想了解的一切。
猜你喜欢
- 2024-10-21 安装oracle12C RAC时可跳过gimr安装
- 2024-10-21 「干货分享」史跃东老师:Oracle Database 12C 之多租户(三)
- 2024-10-21 详解Windows下oracle打补丁步骤 oracle11g打补丁
- 2024-10-21 Oracle Database 12c SQL OCA/OCP 1Z0-071题库(1-5题)
- 2024-10-21 Oracle 19C 数据泵导出的dmp导入 12C 报ORA-39002 错误解决方法
- 2024-10-21 Oracle 12c Release 2 RAC On Oracle Linux 7
- 2024-10-21 产品|QData Cloud 完美兼容 Oracle 12c 容器数据库
- 2024-10-21 docker安装oracle 12c实现数据持久化
- 2024-10-21 一文看懂Oracle11g和12c的v$pwfile_user视图
- 2024-10-21 详解Oracle11g和12c的v$pwfile_user视图及区别
你 发表评论:
欢迎- 05-24网络信息安全之敏感信息在传输、显示时如何加密和脱敏处理
- 05-24常见加密方式及Python实现
- 05-24pdf怎么加密
- 05-24aes256 加密 解密 (python3) 「二」
- 05-24深入理解Python3密码学:详解PyCrypto库加密、解密与数字签名
- 05-24Springboot实现对配置文件中的明文密码加密
- 05-24JavaScript常规加密技术
- 05-24信息安全人人平等 谷歌推出低性能安卓手机加密技术
- 最近发表
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端react (48)
- 前端aes加密 (58)
- 前端md5加密 (49)
- 前端路由 (55)
- 前端数组 (65)
- 前端定时器 (47)
- 前端接口 (46)
- Oracle RAC (73)
- oracle恢复 (76)
- oracle 删除表 (48)
- oracle 用户名 (74)
- oracle 工具 (55)
- oracle 内存 (50)
- oracle 导出表 (57)
- oracle 中文 (51)
- oracle链接 (47)
- oracle的函数 (57)
- mac oracle (47)
- 前端调试 (52)
- 前端登录页面 (48)
本文暂时没有评论,来添加一个吧(●'◡'●)