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

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

sinye56 2024-10-12 10:19 6 浏览 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这个脚本里的内容吧,这里有你想了解的一切。

相关推荐

CTO偷偷传我的系统性能优化十大绝招(万字干货)

上篇引言:取与舍软件设计开发某种意义上是“取”与“舍”的艺术。关于性能方面,就像建筑设计成抗震9度需要额外的成本一样,高性能软件系统也意味着更高的实现成本,有时候与其他质量属性甚至会冲突,比如安全性、...

提升效率!VMware虚拟机性能优化十大实用技巧

我40岁,干跨境婚恋中介的。为服务各国用户,常得弄英语、日语、俄语系统环境,VMware虚拟机帮了不少忙。用久了发现优化下性能,效率能更高。今儿就来聊聊优化技巧和同类软件。一、VMware虚拟...

低延迟场景下的性能优化实践

本文摘录自「全球C++及系统软件技术大会」ScottMeyers曾说到过,如果你不在乎性能,为什么要在C++这里,而不去隔壁的Pythonroom呢?今天我们就从“低延迟的概述”、“低延迟系...

Linux性能调优之内存负载调优的一些笔记

写在前面整理一些Linux内存调优的笔记,分享给小伙伴博文没有涉及的Demo,理论方法偏多,可以用作内存调优入门博文内容涉及:Linux内存管理的基本理论寻找内存泄露的进程内存交换空间调优不同方式的...

优化性能套路:带你战胜这只后段程序员的拦路虎

来源|极客时间《卖桃者说》作者|池建强编辑|成敏你好,这里是卖桃者说。今天给大家推荐一篇文章,来自倪朋飞老师的专栏《Linux性能优化实战》,文章主要讲的是优化性能的套路,这几乎是每个后端程序员...

SK海力士CXL优化解决方案已成功搭载于Linux:带宽提升30%,性能提升12%以上

SK海力士宣布,已将用于优化CXL(ComputeExpressLink)存储器运行的自研软件异构存储器软件开发套件(HMSDK)中主要功能成功搭载于全球最大的开源操作系统Linux上,不但提升了...

Linux内核优化:提升系统性能的秘诀

Linux内核优化:提升系统性能的艺术在深入Linux内核优化的世界之前,让我们先来理解一下内核优化的重要性。Linux内核是操作系统的核心,负责管理系统资源和控制硬件。一个经过精心优化的内核可以显著...

Linux系统性能优化:七个实战经验

Linux系统的性能是指操作系统完成任务的有效性、稳定性和响应速度。Linux系统管理员可能经常会遇到系统不稳定、响应速度慢等问题,例如在Linux上搭建了一个web服务,经常出现网页无法打开、打开速...

腾讯面试:linux内存性能优化总结

【1】内存映射Linux内核给每个进程都提供了一个独立且连续的虚拟地址空间,以便进程可以方便地访问虚拟内存;虚拟地址空间的内部又被分为内核空间和用户空间两部分,不同字长的处理器,地址空间的范围也不同...

Linux文件系统性能调优《参数优化详解》

由于各种的I/O负载情形各异,Linux系统中文件系统的缺省配置一般来说都比较中庸,强调普遍适用性。然而在特定应用下,这种配置往往在I/O性能方面不能达到最优。因此,如果应用对I/O性能要求较高,除...

Nginx 性能优化(吐血总结)

一、性能优化考虑点当我需要进行性能优化时,说明我们服务器无法满足日益增长的业务。性能优化是一个比较大的课题,需要从以下几个方面进行探讨当前系统结构瓶颈了解业务模式性能与安全1、当前系统结构瓶颈首先需要...

Linux问题分析与性能优化

排查顺序整体情况:top/htop/atop命令查看进程/线程、CPU、内存使用情况,CPU使用情况;dstat2查看CPU、磁盘IO、网络IO、换页、中断、切换,系统I/O状态;vmstat2查...

大神级产品:手机装 Linux 运行 Docker 如此简单

本内容来源于@什么值得买APP,观点仅代表作者本人|作者:灵昱Termux作为一个强大的Android终端模拟器,能够运行多种Linux环境。然而,直接在Termux上运行Docker并不可行,需要...

新手必须掌握的Linux命令

Shell就是终端程序的统称,它充当了人与内核(硬件)之间的翻译官,用户把一些命令“告诉”终端程序,它就会调用相应的程序服务去完成某些工作。现在包括红帽系统在内的许多主流Linux系统默认使用的终端是...

Linux 系统常用的 30 个系统环境变量全解析

在Linux系统中,环境变量起着至关重要的作用,它们犹如隐藏在系统背后的“魔法指令”,掌控着诸多程序的运行路径、配置信息等关键要素。尤其在shell脚本编写时,巧妙运用环境变量,能让脚本如虎...

取消回复欢迎 发表评论: