在cdb$root中创建用户必须用c##或c##前缀,否则必报ora-65096;这是oracle 19c cdb架构的硬性内核校验,不依赖参数或配置,仅当切换至pdb后方可创建无前缀本地用户。
直接结论:在cdb$root中创建用户,必须用c##或c##前缀,否则必报ora-65096;这不是bug,是oracle 19c cdb架构的硬性校验。
为什么create user xxx在CDB里一定报ORA-65096
Oracle 19c默认以CDB模式运行,CREATE USER语句在CDB$ROOT容器中执行时,自动视为创建公用用户(common user),而公用用户名必须严格匹配正则^c##|^C##——这个校验写死在内核里,改COMPATIBLE、清回收站、换登录角色都绕不过去。
常见错误现象:
- 用
sqlplus / as sysdba连上后直接create user dsjzx identified by 123;→ 立刻报ORA-65096 - 以为加了
container=all就能豁免命名规则 → 实际上container=all只影响授权范围,不改变用户名校验逻辑
create user c##xxx之后怎么让权限真正生效
公用用户虽在CDB$ROOT创建,但默认只在CDB$ROOT中有基础权限;若想在PDB中操作,必须显式授权,且注意container参数取值:
-
grant connect, resource to c##xxx;→ 仅在当前容器(即CDB$ROOT)授予权限 -
grant connect, resource to c##xxx container=all;→ 向所有已打开的PDB中同名用户授予权限(要求PDB中该用户已存在) - 如果PDB中尚无该用户,
container=all不会自动创建,只会跳过该PDB并警告 - 授权后仍无法在PDB中登录?检查PDB是否处于
OPEN状态:select con_id, name, open_mode from v$pdbs;
如何验证用户确实跨容器可用
不能只查dba_users,它只反映当前容器视图;要确认公用用户是否真正在各PDB中“可见”,得查cdb_users:
SELECT username, con_id, created FROM cdb_users WHERE username LIKE 'C##%';
关键看con_id字段:
-
con_id = 1→ CDB$ROOT中存在 -
con_id = 3(或其他大于1的值)→ 对应PDB中也存在该用户(前提是已执行container=all授权或手动切换到PDB再create user) - 若只看到
con_id = 1,说明用户未在PDB中激活,即使授权了container=all,也可能因PDB未OPEN或用户未被同步
容易忽略的兼容性陷阱
公用用户名字带c##不是临时妥协,而是设计约束;但实际使用中容易踩两个坑:
- PDB中已有同名本地用户(如
dsjzx),又在CDB$ROOT建了c##dsjzx→ 两者完全独立,互不影响,别误以为是“升级”或“覆盖” - 应用连接字符串里写
jdbc:oracle:thin:@host:port/cdb_name,却期望连到PDB里的c##xxx→ 必须明确指定PDB service name,例如jdbc:oracle:thin:@host:port/pdb1.example.com - 用
sqlplus c##xxx/xxx@cdb_name登录,默认进CDB$ROOT;想进PDB必须加@pdb_service_name
最常被忽略的一点:c##前缀会真实出现在user$基表和所有系统视图中,任何依赖username字段做匹配的脚本、监控工具或中间件配置,都得适配这个前缀——它不是显示层伪装,而是存储层事实。











