Oracle 12c 多租戶專題|CDB後設資料內幕
原文連結 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這個指令碼里的內容吧,這裡有你想了解的一切。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/196700/viewspace-2154404/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Oracle 12c系列(一)|多租戶容器資料庫Oracle資料庫
- Oracle多租戶管理員指南-CDB共性概念03Oracle
- 12C多租戶關於CDB、PDB的常用SQL語句SQL
- oracle 12c 多租戶體系結構概念Oracle
- Oracle 18c新特性:多租戶艦隊 CDB FleetOracle
- Part II 建立和配置CDB-Oracle多租戶管理員指南Oracle
- Oracle 12C RAC CDB資料庫部署Oracle資料庫
- Oracle 18c新特性詳解-多租戶專題Oracle
- Part I CDB中容器概述01-Oracle多租戶管理員指南Oracle
- Part I CDB中的Oracle資源管理器概述07-Oracle多租戶管理員指南Oracle
- 2.10.1 在non-CDB(非多租戶)環境中克隆資料庫資料庫
- Part I CDB中服務的概述04-Oracle多租戶管理員指南Oracle
- Part I CDB中表空間和資料庫檔案的概述05-Oracle多租戶管理員指南資料庫Oracle
- 12C多租戶rman備份文件
- Oracle 12c no-CDB轉換為CDBOracle
- Part I CDB中可用性的概述06-Oracle多租戶管理員指南Oracle
- 【資料庫升級】Oracle指令碼升級12c CDB to 19c CDB資料庫Oracle指令碼
- 多租戶:在Oracle12.2中 從Non-CDB遷移到PDB,從PDB遷移另一個CDB中Oracle
- Oracle 12c 手動建立CDB和非CDBOracle
- Part II 配置和管理多租戶環境概述-Oracle多租戶管理員指南Oracle
- MaxCompute多租戶資料安全體系
- Oracle 12c CDB&PDBs管理Oracle
- oracle 12c non-cdb升級成cdb模式Oracle模式
- oracle 12c 多租戶體系結構概念之資料字典、服務、使用者、角色與許可權Oracle
- Oracle 20C 多租戶_新特性Oracle
- Oracle多租戶架構優勢分析Oracle架構
- Oracle 20C 多租戶_1.2 資料庫與例項介紹Oracle資料庫
- 多租戶
- Oracle 12c nocdb轉換成cdbOracle
- Oracle 12c 檢視CDB&PDBs資訊(SQL*PLUS)OracleSQL
- Oracle 12c 建立與刪除CDB、PDBsOracle
- 大資料體系下的多租戶管理方案大資料
- Oracle Database 12cR2多租戶權威指南OracleDatabase
- Part III PDB建立概述-Oracle多租戶管理員指南Oracle
- 【Dataguard】Oracle多租戶環境對Dataguard的影響Oracle
- 技術解讀資料庫如何實現“多租戶”?資料庫
- SpringBoot 專案使用 Mybatis Plus 實現多租戶Spring BootMyBatis
- Oracle 20C 多租戶_體系結構介紹Oracle