百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 优雅编程 > 正文

Oracle 12c 多租户专题|CDB元数据内幕

sinye56 2024-10-12 10:19 4 浏览 0 评论

原文链接 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这个脚本里的内容吧,这里有你想了解的一切。

相关推荐

Linux基础知识之修改root用户密码

现象:Linux修改密码出现:Authenticationtokenmanipulationerror。故障解决办法:进入单用户,执行pwconv,再执行passwdroot。...

Linux如何修改远程访问端口

对于Linux服务器而言,其默认的远程访问端口为22。但是,出于安全方面的考虑,一般都会修改该端口。下面我来简答介绍一下如何修改Linux服务器默认的远程访问端口。对于默认端口而言,其相关的配置位于/...

如何批量更改文件的权限

如果你发觉一个目录结构下的大量文件权限(读、写、可执行)很乱时,可以执行以下两个命令批量修正:批量修改文件夹的权限chmod755-Rdir_name批量修改文件的权限finddir_nam...

CentOS「linux」学习笔记10:修改文件和目录权限

?linux基础操作:主要介绍了修改文件和目录的权限及chown和chgrp高级用法6.chmod修改权限1:字母方式[修改文件或目录的权限]u代表所属者,g代表所属组,o代表其他组的用户,a代表所有...

Linux下更改串口的权限

问题描述我在Ubuntu中使用ArduinoIDE,并且遇到串口问题。它过去一直有效,但由于可能不必要的原因,我觉得有必要将一些文件的所有权从root所有权更改为我的用户所有权。...

Linux chown命令:修改文件和目录的所有者和所属组

chown命令,可以认为是"changeowner"的缩写,主要用于修改文件(或目录)的所有者,除此之外,这个命令也可以修改文件(或目录)的所属组。当只需要修改所有者时,可使用...

chmod修改文件夹及子目录权限的方法

chmod修改文件夹及子目录权限的方法打开终端进入你需要修改的目录然后执行下面这条命令chmod777*-R全部子目录及文件权限改为777查看linux文件的权限:ls-l文件名称查看li...

Android 修改隐藏设置项权限

在Android系统中,修改某些隐藏设置项或权限通常涉及到系统级别的操作,尤其是针对非标准的、未在常规用户界面显示的高级选项。这些隐藏设置往往与隐私保护、安全相关的特殊功能有关,或者涉及开发者选项、权...

完蛋了!我不小心把Linux所有的文件权限修改了!在线等修复!

最近一个客户在群里说他一不小心把某台业务服务器的根目录权限给改了,本来想修改当前目录,结果执行成了根目录。...

linux改变安全性设置-改变所属关系

CentOS7.3学习笔记总结(五十八)-改变安全性设置-改变所属关系在以前的文章里,我介绍过linux文件权限,感兴趣的朋友可以关注我,阅读一下这篇文章。这里我们不在做过的介绍,注重介绍改变文件或者...

Python基础到实战一飞冲天(一)--linux基础(七)修改权限chmod

#07_Python基础到实战一飞冲天(一)--linux基础(七)--修改权限chmod-root-groupadd-groupdel-chgrp-username-passwd...

linux更改用户权限为root权限方法大全

背景在使用linux系统时,经常会遇到需要修改用户权限为root权限。通过修改用户所属群组groupid为root,此操作只能使普通用户实现享有部分root权限,普通用户仍不能像root用户一样享有超...

怎么用ip命令在linux中添加路由表项?

在Linux中添加路由表项,可以使用ip命令的route子命令。添加路由表项的基本语法如下:sudoiprouteadd<network>via<gateway>这...

Linux配置网络

1、网卡名配置相关文件回到顶部网卡名命名规则文件:/etc/udev/rules.d/70-persistent-net.rules#PCIdevice0x8086:0x100f(e1000)...

Linux系列---网络配置文件

1.网卡配置文件在/etc/sysconfig/network-scripts/下:[root@oldboynetwork-scripts]#ls/etc/sysconfig/network-s...

取消回复欢迎 发表评论: