select any table在adg物理备库必然失败,因只读模式禁止隐式字典访问;必须在备库显式逐对象授权,使用角色封装并单独授予create session及临时表空间。

不能用 SELECT ANY TABLE,它在 ADG 物理备库上必然失败,且报错 ORA-01031: insufficient privileges ——这不是权限没刷,是 Oracle 内核硬限制。
为什么 SELECT ANY TABLE 在备库一定不生效
ADG 物理备库(READ ONLY WITH APPLY)禁止任何隐式字典写访问。SELECT ANY TABLE 的权限校验过程会尝试读取底层字典表(如 obj$、tab$),触发只读保护机制。即使 GRANT SELECT ANY TABLE TO ro_user 语句执行成功,后续任意 SELECT 都会直接报 ORA-01031。
同理,SELECT ANY DICTIONARY 也无效;备库不开放“任意读字典”通道。
- 这不是缓存延迟或权限未同步问题,是设计层面的强制隔离
- 主库上授的系统级只读权限,在备库完全不继承
- 唯一可行路径:显式、对象级、带 schema 名的授权
必须在备库上执行的最小安全授权流程
所有操作必须在目标备库实例(或目标 PDB)中完成,不能只在主库操作后等待同步。
- 先确认表真实存在:
SELECT owner, table_name FROM dba_tables WHERE owner = 'CMSPROD' AND table_name = 'PORTAL_USER_PROFILE'; - 创建用户并赋予连接能力:
CREATE USER cmsreadonly IDENTIFIED BY "xxx" DEFAULT TABLESPACE users;+GRANT CREATE SESSION TO cmsreadonly; - 逐表授权(schema 名不可省略,且必须大写):
GRANT SELECT ON CMSPROD.PORTAL_USER_PROFILE TO cmsreadonly; - 若需覆盖整个 schema,用查询生成脚本(在备库执行):
SELECT 'GRANT SELECT ON CMSPROD.' || table_name || ' TO cmsreadonly;' FROM dba_tables WHERE owner = 'CMSPROD';
用角色封装比直授用户更可控,但要注意多租户陷阱
直接对用户授一堆 GRANT SELECT ON ... 极难维护,漏一张表就查不到数据。角色是更合理的抽象层。
- 在备库的目标 PDB 中执行(先
ALTER SESSION SET CONTAINER = ORA19CPDB;):CREATE ROLE app_readonly; - 把权限授给角色,不是用户:
GRANT SELECT ON CMSPROD.PORTAL_USER_PROFILE TO app_readonly; - 再把角色授给用户:
GRANT app_readonly TO cmsreadonly; -
CREATE SESSION必须单独授予用户,角色本身不带登录能力 - 角色不会跨 PDB 自动继承 —— CDB 环境下,每个需访问的 PDB 都要独立建角色、独立授权
同义词、视图、临时表空间这些容易被忽略的点
只读用户能查基表,不代表能查视图或同义词;能连上,不代表能跑报表。
- 视图和同义词需要单独授权:
GRANT SELECT ON CMSPROD.VW_USER_REPORT TO cmsreadonly;(不能指望基表权限自动透传) - 临时表空间必须手动添加 tempfile:
ALTER TABLESPACE temp ADD TEMPFILE '/u01/oradata/ORA19CPDB/temp02.dbf' SIZE 100M;,否则报表类查询可能报ORA-25153 - 用户锁状态由内存视图
V_$RO_USER_ACCOUNT管理,备库上密码输错锁定后,需在备库执行ALTER USER xxx ACCOUNT UNLOCK;解锁(Oracle 12c+ 支持)
真正落地时,最耗时的不是写 SQL,而是确认每张表、每个视图、每个同义词、每个 PDB、每个临时文件是否都已显式覆盖 —— 没有捷径,也没有“全局只读权限”这种东西。











