前言:
Oracle 12c的多租戶(multitenant)環境與SQL Server的架構非常相似,CDB$ROOT類似於master、PDB$SEED類似於model、各個pluggable database就相當於普通的業務庫。
官方宣傳了12c的很多好處但其實沒卵用,12c設想通過合並多個庫來避免DBLINK的性能問題,但其實還是通過壓榨單一服務器來實現的,其他好處諸如PDB遷移方便、JSON支持、資源組、索引壓縮、in-memory支持等等要么是炒冷飯,要么實際適用場景很少,不過這些新特性都還有待繼續研究,一步一步來。
首先必須要解決的是登錄問題,12c的多租戶環境下用戶與以前不同了,以前的用戶性質都是一樣的,而在12c中一個實例下可以有多個可插拔的數據庫,因此用戶管理與之前就有很大區別了,本文介紹12c用戶及其管理,以12.2.0.1版本為例,所有觀點來源於如下官網鏈接:
本文主要包含以下六個部分:
- 關於用戶安全的簡易說明
- 創建用戶
- 更改用戶
- 配置用戶資源限額
- 刪除用戶
- 用戶和profile的的數據字典
每個數據庫都有自己的用戶列表,在創建用戶時你可以為這個用戶設置一系列的登錄limits或resource limits,或者使用profile實現此功能,profile就是一個用戶所擁有的屬性的集合,你可以查看dba_profiles來詳細了解。
此外你還可以為用戶賦予一系列的權限和角色,參考:Configuring Privilege and Role Authorization
二、創建用戶
這部分涉及的內容很多,保險起見還是全部列出來的比較好。
- 關於Common User和Local User
在多租戶的環境中,common user可以訪問整個CDB而local user只能訪問特定的PDB,關於CDB與PDB的關系這里用官網的一幅圖來表示:

可以看到CDB其實就是指實例下的所有數據庫,包含ROOT、Seed和其他所有PDBs,整個Instance可以看做一個容器,即Container DB(CDB).
你可以使用sysdba用戶來啟動PDB,有以下兩種方式:
1.alter session set container=orclpdb1; alter database open; --對應關閉方式為shutdown immediate; 2.alter pluggable database orclpdb1 open; --對應關閉方式為alter pluggable database orclpdb1 close; Ps:PDB的關閉是指將PDB由open變為mount狀態,徹底shutdown需要到root中關閉整個實例。
關於Common user:
Common user必須以C##或者c##開頭,你可以通過修改common_user_prefix參數來更改這個prefix,而Local user則必須避免以這個prefix開頭。
Common user的身份和密碼對於每個PDB來說都是可見的,只要有合適的權限common user也可以操作PDB。
Common user通常用於在root中操作PDBs,例如插拔PDB數據庫、更改PDB狀態或者為CDB指定臨時表空間等。
Common user只能在cdb$root中創建,cdb$root中也只能創建common user。
舉例來講,common user可做的操作有:創建或修改common/local user、向其他common/local user賦權限或者賦角色、對整個CDB執行ALTER DATABASE開頭的恢復語句、通過ALTER PLUGGABLE DATABASE語句來來更改PDB的狀態(必須是連接到CDB$ROOT庫時)。當然一個local user如果有權限也可以更改其自身PDB的狀態。
Oracle的系統用戶如sys、system等,就是common user,即可操作ROOT數據庫也可以操作任意PDB,除非某個PDB開啟Vault-enabled來限制系統用戶的權限。
當你將一個non-CDB的數據庫作為PDB插入到CDB中時:
- non-CDB的系統用戶將會與CDB common user合並,即non-CDB中的系統用戶將會新增到CDB中,如果username沖突則不新增,這些沖突系統用戶的密碼全部按照CDB的來,原密碼失效。
- 如果你曾經更改過原non-CDB系統用戶的權限,那么合並之后這些差異權限只對這個PDB生效,對其他PDB無效。這里還不知道如果少了權限合並之后是不是依然少,有需要的時候再測試下好了。
當你將一個PDB插入CDB時:
- 原PDB的common user會失去它所有的公共權限,包括set container的權限。
- 如果目標CDB有與PDB同名的common user,那么這兩個common user合並,CDB的common user密碼優先生效。而PDB中其他不同名的common user將會被鎖定。你可以對這些鎖定的common user做如下任意一種處理:
- 不處理這些locked用戶,在權限適當的情況下你依然可以使用這些schema下的對象。
- 使用EXPDP導出這些用戶下的數據並導入到另一個用戶下,然后刪掉此locked user。
- 關掉PDB,連接到CDB$ROOT,創建同名的common user,然后重開PDB,Oracle會自動處理權限差異,此時你可以unlock這些用戶,他們的本地權限保持不變。
關於Local user:
Local user就是只存在於PDB中的用戶,local user可以擁有管理類權限,但是這類權限只對local user所在的PDB有效。
Local user與common user的區別可以概括為以下幾點:
- Local user不能創建common user,也不能在cdb$root中被授予權限。而common user只要有合適的權限,就能創建、修改common/local user,就能公共的或本地的grant/revoke權限。Local user如果權限充足,可以創建、修改local user,也可以在PDB中向common/local user賦權限。
- 你可以向local user賦予公共角色或權限(類似於select any table這種),但是這些公共角色或權限包含的權限只對其所屬PDB有效。
- Local user在一個PDB中應當是唯一的。
- 如果local user有合適的權限,那么它也可以訪問common user里的對象。
- Local user只能在PDB中被創建,PDB中也只能創建local user而不能創建common user。
- 你可以根據版本來選擇是否禁用local user,但是卻不能這么操作common user。
- 創建用戶示例
$ sqlplus / as sysdba SQL> show pdbs; CON_ID CON_NAME OPEN MODE RESTRICTED ---------- ------------------------------ ---------- ---------- 2 PDB$SEED READ ONLY NO 3 ORCLPDB1 READ WRITE NO 4 ORCLPDB2 MOUNTED SQL> show con_name CON_NAME ------------------------------ CDB$ROOT SQL> alter pluggable database orclpdb1 open; Pluggable database altered. SQL> create user c##test identified by test container=all; --common user只能以c##開頭,且強制container=all,創建時可以不用寫container=all。 SQL> grant dba to c##test; --至此common user c##test創建完畢,但是目前為止這個common user只能操作CDB$ROOT schema對象下的objects和創建PDB的local user,並沒有操作PDB下objects的權限,一般來說common user不必有此權限,但是如果你想擁有,那么: SQL> alter session set container=orclpdb1; --切換至orclpdb1的PDB后再次grant權限 SQL> grant dba to c##test; --至此common user c##test就擁有了操作PDB orclpdb1下objects的權限了。但是這種common user刪除時必須添加cascade: SQL> drop user c##test; drop user c##test * ERROR at line 1: ORA-65048: error encountered when processing the current DDL statement in pluggable database ORCLPDB1 ORA-01922: CASCADE must be specified to drop 'C##TEST' SQL> drop user c##test cascade; User dropped.
創建local user:
$ sqlplus / as sysdba SQL> alter session set container=orclpdb1; SQL> create user test identified by test default tablespace users quota 100M on example quota 200M on test temporary tablespace temp profile default container=current; --local user只能以非c##開頭,且強制container=current,創建時可以不用寫container=current。 SQL> grant connect,resource,unlimited tablespace to test; $ sqlplus test/test@localhost/orclpdb1 --至此local user創建完畢。 --需要特別注意的一點是:12c的resource角色中已經不包含unlimited tablespace權限因此需要單獨賦予,或者通過quota項來分配一定的表空間限額。
- 創建用戶總結:
一般來說沒有必要創建除系統用以外的common user,或者只需要創建一個不含操作具體PDB權限的common user,common user不應當用於操作具體業務數據,正如官方說明的那樣,common user通常的操作應當是:
創建或修改common/local user、向其他common/local user賦權限或者賦角色、對整個CDB執行ALTER DATABASE開頭的恢復語句、通過ALTER PLUGGABLE DATABASE語句來來更改PDB的狀態(必須是連接到CDB$ROOT庫時)。
我們可以使用如下SQL來查看PDB或CDB$ROOT庫內的用戶:
$ sqlplus / as sysdba SQL> alter session set container=orclpdb1; SQL> set lines 200 SQL> set pages 100 SQL> col username for a40 SQL> select USERNAME,ACCOUNT_STATUS,COMMON from dba_users; --結果可以看到common user是在所有PDB中可見的,在單個PDB中查詢dba_users你能看到所有common user和屬於本PDB的local user。
再來考慮一種特殊情況,假如我們需要在一個PDB中訪問另一個PDB中的數據呢?
使用local user是不可能的,因為local user完全PDB隔離,就算local user被賦予了類似dba、select any table這些公共權限,也只是在PDB內有效,無法跨庫訪問,那么只能使用common user。
接下來舉例common user跨PDBs訪問:
SQL> show pdbs; CON_ID CON_NAME OPEN MODE RESTRICTED ---------- ------------------------------ ---------- ---------- 2 PDB$SEED READ ONLY NO 3 ORCLPDB1 READ WRITE NO 4 ORCLPDB2 READ WRITE NO --我們有兩個PDB:orclpdb1和orclpdb2都是open狀態,各自有一個scott用戶,用戶下分別有一個test1、test2表。我們先創建一個common user: $ sqlplus / as sysdba SQL> create user c##scott identified by tiger; SQL> grant connect,resource,unlimited tablespace,select any table,create view to c##scott; --然后在orclpdb1中給c##scott賦權限 SQL> alter session set container=orclpdb1; SQL> grant connect,resource,unlimited tablespace,select any table,create view to c##scott; --接下來在orclpdb2中給c##scott賦權限 SQL> alter session set container=orclpdb2; SQL> grant connect,resource,unlimited tablespace,select any table,create view to c##scott;
至此c##scott用戶就有了在兩個pdb中查詢的權限,但是此權限是嚴格PDB隔離的,不能跨庫查詢。雖然不能像SQL Server那樣跨庫直接查詢,不過oracle卻提供了變通的辦法--CONTAINERS子句,參考網址如下(此特性開始於12.1.0.2版本):
舉例:
SELECT ename FROM CONTAINERS(scott.emp) WHERE CON_ID IN (45, 49);
想要使用CONTAINERS子句必須滿足以下條件:
1.CONTAINERS子句中的表、視圖或同義詞必須在ROOT和所有PDB中都存在。
2.CONTAINERS子句中涉及的表、視圖的屬主必須是執行SQL語句的common user;如果是同義詞,那么這個同義詞必須是引用的此common user下的表或視圖。
3.包含CONTAINERS子句的SQL必須在CDB$ROOT數據庫下使用common user執行。
如果CONTAINERS子句涉及的表里有以下列類型,那么會被自動過濾掉:
1.用戶定義的類型:object types, varrays, REFs, and nested tables
2.系統類型:ANYTYPE, ANYDATASET, URI types, SDO_TOPO_GEOMETRY, SDO_GEORASTER, and Expression
看完以上5條要求后突然發現這些要求很奇葩,如果CONTAINERS子句中的表、視圖或同義詞必須在ROOT和所有PDB中都存在,那我還為什么還要跨PDB查詢,直接在ROOT中查不就得了?
稍微思考和實踐了一下,發現使用CONTAINERS子句的條件應該是:
- 必須使用common user在CDB$ROOT中查詢
- CONTAINERS()方法的輸入是表、視圖或同義詞,這個對象必須在CDB$ROOT中有個名稱+結構完全相同的對象,無論是表還是視圖還是同義詞,表可以沒有數據。
- 必須使用執行此查詢的common user到表所在的PDB中創建一個同名view,這需要common user擁有查詢此表的權限。
那么接着上邊的實例就可以很容易的進行跨PDB查詢了:
1.首先我們使用common user c##scott在orclpdb1中創建針對scott.test1的視圖: $ sqlplus c##scott/tiger@orclpdb1 SQL> create view test1 as select * from scott.test1; 2.然后使用common user c##scott在CDB$ROOT中創建同名的表或者視圖: $ sqlplus c##scott/tiger SQL> create table test1(name varchar2(20)); 3.此時我們就可以使用SQL查詢了: SQL> select * from CONTAINERS(test1) where con_id=3; 4.如果還需要聯結查詢orclpdb2中的test2表,那么: $ sqlplus c##scott/tiger@orclpdb2 SQL> create view test2 as select * from scott.test2; $ sqlplus c##scott/tiger SQL> create table test2(name varchar2(20)); 5.這樣就相當於test1和test2在CDB$ROOT中都有了一個接口,我們通過接口訪問相應PDB中的common user下的視圖,視圖又是直接定義在最終表上的。
畫一幅圖來直觀的表示:

三、修改用戶
修改用戶與創建用戶的語法很相似:
--一個官方示例: ALTER USER c##hr_admin DEFAULT TABLESPACE data_ts TEMPORARY TABLESPACE temp_ts QUOTA 100M ON data_ts QUOTA 0 ON test_ts SET CONTAINER_DATA = (emp_db, hr_db) FOR V$SESSION CONTAINER = CURRENT; --有以下幾個地方需要解釋: 1.QUOTA的大小表示在表空間上限額的總大小,而非增/減量。 2.SET CONTAINER_DATA表示此common user在root中訪問v$session時可以獲取到emp_db和hr_db相關的session信息。
此外一般用戶的密碼可以通過alter user語句修改,而sys密碼可以通過orapwd工具修改:
orapwd file='orapworcl' force=y orapwd input_file='orapworcl' file='orapwd' sys=y force=y
四、配置用戶資源限額
這點可以通過一個視圖很容易的看到:
因此修改用戶資源限額就很簡單了,例如:
alter user scott limit password_life_time unlimited; --或者新建一個profile,然后將其指定為用戶的profile: CREATE PROFILE profile_scott LIMIT FAILED_LOGIN_ATTEMPTS 6 PASSWORD_LIFE_TIME 60 PASSWORD_REUSE_TIME 60 PASSWORD_REUSE_MAX 5 PASSWORD_LOCK_TIME 1/24 PASSWORD_GRACE_TIME 10 PASSWORD_VERIFY_FUNCTION DEFAULT; alter user scott profile profile_scott;
五、刪除用戶
略
六、用戶和profile涉及的數據字典
ALL_OBJECTS Describes all objects accessible to the current user ALL_USERS Lists users visible to the current user, but does not describe them DBA_PROFILES Displays all profiles and their limits DBA_TS_QUOTAS Describes tablespace quotas for users DBA_OBJECTS Describes all objects in the database DBA_USERS Describes all users of the database DBA_USERS_WITH_DEFPWD Lists all user accounts that have default passwords PROXY_USERS Describes users who can assume the identity of other users RESOURCE_COST Lists the cost for each resource in terms of CPUs for each session, reads for each session, connection times, and SGA USER_PASSWORD_LIMITS Describes the password profile parameters that are assigned to the user USER_RESOURCE_LIMITS Displays the resource limits for the current user USER_TS_QUOTAS Describes tablespace quotas for users USER_OBJECTS Describes all objects owned by the current user USER_USERS Describes only the current user V$SESSION Lists session information for the current database session V$SESSTAT Displays user session statistics V$STATNAME Displays decoded statistic names for the statistics shown in the V$SESSTAT view