Oracle 对象依赖性与对象失效

一、 起源

开发A在从库查询视图遇到报错:

执行的SQL为: select * from V_TEST_JOIN 
具体错误信息为:java.sql.SQLSyntaxErrorException: 
ORA-04045: 在重新编译/重新验证 V_TEST_JOIN 时出错
ORA-16000: 数据库或可插入数据库是以只读访问方式打开的

二、 报错原因及处理方法

1. 报错原因

视图V_TEST_JOIN失效,在select时会自动编译,但访问的是从库,而从库是只读的,因此编译会报错ORA-16000: 数据库或可插入数据库是以只读访问方式打开的

2. 处理方法

报错处理方法极其简单,在主库编译失效对象即可。常见编译方法如下:

  • 在主库查询该对象由其自动编译
  • ALTER … COMPILE;手动编译
ALTER PACKAGE my_package COMPILE;
ALTER PACKAGE my_package COMPILE BODY;
ALTER PROCEDURE my_procedure COMPILE;
ALTER FUNCTION my_function COMPILE;
ALTER TRIGGER my_trigger COMPILE;
ALTER VIEW my_view COMPILE;

也可以生成重编译的批量脚本后一起执行:

SELECT 'ALTER '||OBJECT_TYPE||' '||OBJECT_NAME ||' COMPILE;'
  FROM USER_OBJECTS
 WHERE STATUS='INVALID';
  • utlrp.sql脚本

执行@$ORACLE_HOME/rdbms/admin/utlrp.sql脚本编译数据库失效对象。

数据库的升级迁移等操作可能会导致数据库中大量对象失效,由于对象间可能存在复杂的依赖关系,手工编译通常无法顺利通过。Oracle强烈建议在升级迁移后,运行此脚本编译失效对象。utlrp.sql中调用了$ORACLE_HOME/rdbms/admin/utlrcmp.sql来编译失效对象

此脚本需要用SQLPLUS以SYSDBA身份运行,运行时中最好不要有活动事物或DDL操作,否则极容易导致死锁的出现。

 

  • DBMS_DDL包 (只用于PL/SQL对象,不能编译视图):
EXEC DBMS_DDL('PACKAGE','MY_SCHEMA','MY_PACKAGE'); 
EXEC DBMS_DDL('PACKAGE BODY','MY_SCHEMA','MY_PACKAGE'); 
EXEC DBMS_DDL('PROCEDURE','MY_SCHEMA','MY_PROCEDURE'); 
EXEC DBMS_DDL('FUNCTION','MY_SCHEMA','MY_FUNCTION'); 
EXEC DBMS_DDL('TRIGGER','MY_SCHEMA','MY_TRIGGER');
  • 使用DBMS_UTILITY.compile_schema

ORACLE提供了自动编译的接口调用这个过程会编译指定schema下所有失效的视图、存储过程、函数、触发器、包

exec dbms_utility.compile_schema('SCOTT')
  • UTL_RECOMP.RECOMP_PARALLEL、UTL_RECOMP.RECOMP_SERIAL分别用并行或串行方式进行编译
-- Schema level.EXEC UTL_RECOMP.recomp_serial('SCOTT');
EXEC UTL_RECOMP.recomp_parallel(4,'SCOTT');

 -- Database level.EXEC UTL_RECOMP.recomp_serial();
EXEC UTL_RECOMP.recomp_parallel(4);

 -- Using job_queue_processes value.EXEC UTL_RECOMP.recomp_parallel();
EXEC UTL_RECOMP.recomp_parallel(NULL,'SCOTT');

UTL_RECOMP包的一些使用限制:

(1) 并行执行使用的是job队列,当运行并行编译的时候所有job都会被disable直到编译完成

(2) 包必须在sqlplus中以sysdba权限用户运行

(3) UTL_RECOMP依赖于DBMS_STANDARD、DBMS_JOB、DBMS_RANDOM

(4) 在运行这个包时执行DDL语句可能会导致死锁.

 

三、 如何找到对象失效时间及原因

1. 如果对象是因为编译报错失效的,可以用以下sql查出

select object_name, status, last_ddl_time, e.line, e.text
  from dba_objects d, dba_errors e
 where d.status = 'INVALID'
   and d.OBJECT_NAME = e.name
   and d.object_type = e.type
   and d.owner = e.owner

2. 如果是因为引用对象被修改

可以使用以下方法大概判断,不一定准确(但是也能压到一个较小的时间范围去定位)

思路:对象失效是由于其引用对象更改,可以通过DBA_DEPENDENCIES找到失效对象的引用对象,再由dba_objects找到引用对象的last_ddl_time (ldt1),大于失效对象last_ddl_time (ldt2)并且离其最近的时间一般就是该对象失效的时间(ldt3)。去找ldt3时对应的ddl审计日志,对应sql语句一般就是导致对象失效的原因。

 

下面是个实际的例子

-- 启用enable_ddl_logging
SQL> show parameter ddl

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
ddl_lock_timeout                     integer     0
enable_ddl_logging                   boolean     FALSE

SQL> alter system set enable_ddl_logging=true;
System altered.

SQL> show parameter ddl

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
ddl_lock_timeout                     integer     0
enable_ddl_logging                   boolean     TRUE

有一个失效对象V_TEST_JOIN,last_ddl_time (ldt2)为2019/9/26 15:21:58

查看其引用对象

select * from dba_dependencies d where d.name='V_TEST_JOIN';

查看引用对象last_ddl_time (ldt1)

select d.object_name,d.status,d.last_ddl_time from dba_objects d where d.OBJECT_NAME in('TEST01','TEST02');

可以看到大于ldt2且离其最近的时间ldt3是2019/9/26 15:23:38,这个时间应该就是对象真正失效的时间。

根据ldt3去找ddl日志

cd $ORACLE_BASE/diag/rdbms/conuat/conuat/log

less ddl_conuat.log
…
Thu Sep 26 15:23:38 2019
diag_adl:
alter table TEST02 add testcol number

所以失效的原因是基表TEST02加了列

这里有另外一个问题,在反向测试给表加列会不会导致视图失效的时候,发现基表加列后测试的视图并没有失效,甚至给V_TEST_JOIN视图基表TEST01加列的时候视图也不会失效。

为什么会有这种现象,这就需要去看对象失效究竟是怎么定义的,究竟什么操作才会导致对象失效。

这个答案我们放在最后。

 

四、 对象依赖性

1. 依赖关系概述

某些类型的对象可以在其定义中引用其他对象。如果对象A的定义引用了对象B,则A是(B的)依赖对象,而B是(A的)引用对象。( If the definition of object A references object B, then A is a dependent object (of B) and B is a referenced object (of A).

Oracle自动跟踪和管理依赖关系,以确保依赖对象A相对于其引用对象B而言始终是最新的。当B以可能影响A的方式更改时(通常是DDL语句),A会被标记为失效。在使用A之前,必须针对B的新定义重新编译失效的依赖对象。当引用失效的依赖对象时,它会自动重新编译。由于这种自动管理,A永远不会使用过时的B版本,并且在更改B之后几乎不需要显式重新编译A。

 

2. 依赖关系查看

通过USER_DEPENDENCIESALL_DEPENDENCIESDBA_DEPENDENCIES视图查询对象间依赖关系

utldtree.sql脚本可创建视图DEPTREE和IDEPTREEDEPTREE中包含对象依赖关系树的信息,而IDEPTREE则为格式化后的DEPTREE

3. 对象状态

状态含义

Valid

对象已成功编译

Compiled with errors

编译对象时产生了错误

Invalid

对象被标记为失效,因为它引用的对象已更改(只有依赖对象状态才会为Invalid

Unauthorized

对引用对象的访问权限已被回收(只有依赖对象状态才会为Unauthorized

注意:USER_OBJECTS,ALL_OBJECTS和DBA_OBJECTS不能区分“Compiled with errors、Invalid和“Unauthorized,而将这些状态统一描述为INVALID

 

五、 对象失效

1. 失效影响

对象失效通过以下方式影响应用程序:

  • 失效对象必须先重新验证/编译才能被应用程序使用,会增加应用程序执行的延迟。如果失效对象的数量很大,则首次执行时增加的延迟可能会很大
  • 存储过程、函数和包的失效可能会导致并发执行这些对象的其他会话异常(通常是在等待失效对象编译而被大量阻塞)
  • 如果会话A运行应用程序的同时,在另一个会话B打了补丁,则执行应用程序的会话A会发现使用中的对象已失效,并引发以下4个异常之一:ORA-04061,ORA-04064,ORA-04065或ORA-04068,必须在重启应用程序会话来解决

2. 级联失效

如果对象A依赖于对象B,而对象B依赖于对象C,则A直接依赖于B,B直接依赖于C,而A间接依赖于C。A->B->C

  • 对于直接依赖,只有更改了对影响它们的引用对象操作会使它们失效。
  • 对于间接依赖,它们可能因为引用对象的失效而失效。例如更改C使B失效,则也将使A(还有B的所有直接和间接依赖对象)失效。这就是所谓的级联失效

3. 粗粒度失效与细粒度失效

在11g前的版本,对象依赖项是在对象级别进行管理的,更改引用对象会自动使所有依赖对象失效,这称为粗粒度失效。11g开始引入了细粒度依赖管理新特性,更改引用对象时,只有直接影响依赖对象的更改才会导致依赖对象失效。

下面是会导致粗、细粒度失效的操作列表:

  • 未在表中列出的所有DDL操作均会导致粗粒度失效
  • 左列中的操作会导致细粒度的失效(操作对象不符合右列情况)
  • 当执行左列操作且对象符合右列情况时,会导致粗粒度失效

操作

例外

ALTER TABLE table ADD column

  1. 依赖对象使用SELECT * from table(视图除外)
  2. 依赖对象使用table%rowtype
  3. 依赖对象执行INSERT时未指定具体列名(insert into test values(1,2,3);)
  4. 依赖对象是查询中join连接的非基础表(不是全部列都显示的那张表)
  5. 依赖对象在查询中使用了PL/SQL变量

ALTER TABLE table {MODIFY|RENAME|DROP|SET UNUSED} column

ALTER TABLE table DROP CONSTRAINT not_null_constraint

  1. 依赖对象直接引用column
  2. 依赖对象使用SELECT * from table
  3. 依赖对象用使用table%rowtype
  4. 依赖对象执行INSERT时未指定具体列名
  5. 依赖对象是依赖于整行的触发器(即未在定义中指定具体列)

CREATE OR REPLACE VIEW view

 

Online Table Redefinition(DBMS_REDEFINITION)

  1. 新旧定义列的列表不同,且符合下列至少一项:
  2. 新视图或表定义中的依赖对象引用列已被修改或删除
  3. 依赖对象使用view%rowtype或table%rowtype
  4. 依赖对象执行INSERT时未指定具体列名
  5. 新视图定义引入了新列,查询中的依赖对象引用视图或包含联接的表。
  6. 新视图定义引入了新列,且依赖对象是查询中join连接的非基础表
  7. 新视图定义引入了新列,且依赖对象在查询中使用了PL/SQL变量
  8. 依赖对象使用RELIES ON子句引用视图或表。

CREATE OR REPLACE SYNONYM synonym

  1. 新旧synonym目标不同,其中一个不是表。
  2. 新旧synonym目标都是表,但这些表的列不同或授权不同。
  3. 新旧synonym目标都是表,且依赖对象是一个视图,该视图引用了一个列,该列在旧目标上是唯一索引的一部分,而在新目标上不是。

DROP INDEX

  1. 该索引是基于函数的索引且依赖对象是触发器。依赖对象是依赖于整行或者使用了函数列的触发器
  2. 索引是唯一索引,依赖对象是视图,且该视图引用了该唯一索引中的列

CREATE OR REPLACE {PROCEDURE|FUNCTION}

  1. 更改调用签名。调用签名是指参数列表(参数顺序、名称和类型)、返回类型,ACCESSIBLE BY子句(白名单),纯度、确定性(determinism),并行性、流水线(pipelining)和实现属性(需要存储过程或函数是用C或Java实现)

CREATE OR REPLACE PACKAGE

  1. ACCESSIBLE BY 子句、包纯度更改
  2. 依赖对象引用已删除或重命名的包或子项
  3. 依赖对象引用的存储过程或函数调用签名或entry-point number已更改
  4. 如果引用的存储过程或函数具有多个重载候选,任何重载候选的调用签名或entry-point number发生了更改,或者添加或删除了一个候选,依赖对象将失效
  5. 依赖对象引用了调用签名、entry-point number或行类型已更改的包游标
  6. 依赖对象引用了定义已更改的package type or subtype
  7. 依赖对象引用了名称、数据类型、初始值或偏移数已更改的包变量或常量

 

六、 对象重新验证

失效的对象必须经过验证才能重新使用。访问对象时,验证会自动进行,不需用户操作。

1. 重新验证编译有错误的对象

编译器无法自动重新验证编译时出错的对象。使用编译器重新编译该对象,如果重新编译没有错误,将对其进行重新验证,否则它仍旧失效。

2. 重新验证未授权的对象

编译器检查依赖对象是否有对引用的访问权限。若有,编译器将重新验证未经授权的对象,而无需重新编译它,否则编译器会报错。

3. 重新验证失效的SQL对象

SQL编译器重新编译失效的对象。如果重新编译没有错误,将对其进行重新验证,否则它仍旧失效。

4. 重新验证失效的PL/SQL对象

对于失效的PL/SQL程序单元(过程,函数或程序包),PL/SQL编译器检查是否有引用对象以影响失效对象的方式进行更改。

  • 若有,编译器将重新编译失效的对象。如果重新编译没有错误,将对其进行重新验证,否则它仍旧失效
  • 若没有,编译器仅重新验证失效的对象而不进行重新编译

 

七、 疑问的答案

看到这里,我们已经找到了开头那个问题的答案。为什么给基表加列有时视图会失效有时不失效?

V_TEST的定义是

CREATE OR REPLACE VIEW V_TEST AS select * from test092502;

对于单表,加列不会导致视图失效(但是视图中也查不到新加列)

V_TEST_JOIN的定义是

CREATE OR REPLACE VIEW V_TEST_JOIN AS
SELECT a."ID",a."AGE",a."TESTCOL",b.name FROM TEST01 a left join TEST02 b on a.id=b.id;

所以给TEST02加列视图失效而给TEST01加列视图不失效

 

参考

https://blog.csdn.net/cymm_liu/article/details/26562451

https://docs.oracle.com/database/121/ADFNS/adfns_dependencies.htm#ADFNS99992

https://docs.oracle.com/database/121/ADMIN/general.htm#ADMIN12491

https://oracle-base.com/articles/11g/enhanced-finer-grained-dependency-management-11gr1

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hehuyi_In

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值