在logzgh的blog上看到《在obj$基表中大量的non-existent类型对象是咋回事?》。这里简单解释下non-existent object和negative dependency。
对于Non-existent object,有两种来源,一种是drop或者rename object留下的,一种则是因为negative dendency机制。在我的试验中,这两种情况实际上都和synonym有关。
第一种情况,对于table,view等对象的drop/rename,我没有试验到non-existent的存在,但是在删除synonym时,可以看到Obj$实际上没有删除该synonym的记录,只是将其type#从5修改成10,也就是变成了non-existent
表已创建。
NING@ ning>grant select on test_synonym to test;
授权成功。
TEST@ ning>create synonym test_synonym for ning.test_synonym;
同义词已创建。
SYS@ ning>select obj#,name,type# from obj$ where name='TEST_SYNONYM';
OBJ# NAME TYPE#
---------- ------------------------------ ----------
44651 TEST_SYNONYM 2
44652 TEST_SYNONYM 5
其中type#=2的是table,type#=5的是synonym
同义词已删除。
SYS@ ning>select obj#,name,type# from obj$ where name='TEST_SYNONYM';
OBJ# NAME TYPE#
---------- ------------------------------ ----------
44651 TEST_SYNONYM 2
44652 TEST_SYNONYM 10
这种情况,在数据库没有重启之前,再次创建该synonym,可以重用obj#,也就是讲type#再修改回来,这样就不会因为不停的删除重建对象导致obj$出现太多的垃圾。重启的时候,这些non-existent应该是由smon清理掉的。
同义词已创建。
SYS@ ning>select obj#,name,type# from obj$ where name='TEST_SYNONYM';
OBJ# NAME TYPE#
---------- ------------------------------ ----------
44651 TEST_SYNONYM 2
44652 TEST_SYNONYM 5
SYS@ ning>shutdown immediate;
数据库已经关闭。
已经卸载数据库。
ORACLE 例程已经关闭。
SYS@ ning>startup
ORACLE 例程已经启动。
Total System Global Area 130023424 bytes
Fixed Size 1247660 bytes
Variable Size 79693396 bytes
Database Buffers 41943040 bytes
Redo Buffers 7139328 bytes
数据库装载完毕。
数据库已经打开。
SYS@ ning>select obj#,name,type# from obj$ where name='TEST_SYNONYM';
OBJ# NAME TYPE#
---------- ------------------------------ ----------
44651 TEST_SYNONYM 2
至于第二种情况,则需要解释下什么是negative dependency。
对于一个查询中的对象,oracle是按照以下顺序来解析的:首先是current schema下找相应的对象如table,view或者private synonym,如果还没有就找public synonym,再没有就报ORA-00942了。
而对于依赖其他对象的对象,在被依赖对象出现变更后,可能会失效。这就需要有一种机制来保证oracle能对依赖关系进行跟踪。如果依赖的对象是current schem下的表,视图或者private synonym,则没有什么问题。如果是public synonym,则需要考虑的不仅是public synonym的变更会造成依赖其的对象失效,当依赖其的对象的schema下出现和public synonym相同名称的对象时,oracle应该是优先使用schema的对象,所以也需要使这些对象失效,以便重新解析到表或者视图上,private synonym没有这种问题,因为不允许在schema下出现同名的private synonym和table/view。这种情况下,oracle会在schema下创建一个non-existent对象,然后在该对象上创建一个依赖,如果以后schema下创建了同名的表或者视图,则non-existent对象被修改成实际创建的对象,使得依赖其的对象失效。Non-existent对象上的依赖就是negative denpendency。
上面讲的有些拗口,我自己都看得有点糊涂了,还是举个例子来看,注意不同语句是在不同用户下执行的:
表已创建。
NING@ ning>grant select on test_negative to test;
授权成功。
TEST@ ning>create public synonym test_negative for ning.test_negative;
同义词已创建。
TEST@ ning>create view v_test_negative
2 as
3 select * from test_negative;
视图已创建。
此时,由于test用户下没有叫test_negative的表或者视图,会通过public synonym来解析该对象,则会产生一个non-existent对象和一个negative dependency
OBJ# OWNER# NAME TYPE#
---------- ---------- ------------------------------ ----------
44662 1 TEST_NEGATIVE 5
44661 42 TEST_NEGATIVE 2
44663 43 TEST_NEGATIVE 10
SYS@ ning>select obj#,name,type# from obj$ where name='V_TEST_NEGATIVE';
OBJ# NAME TYPE#
---------- ------------------------------ ----------
44664 V_TEST_NEGATIVE 4
SYS@ ning>select d_obj#,p_obj# from dependency$
2 where d_obj#=44664;
D_OBJ# P_OBJ#
---------- ----------
44664 44663
44664 44662
可以看到,v_test_dependency出现了两个依赖,一个是对owner#为42的ning.test_negative表的依赖,一个则是对OWNER#为43的non-existent的test.test_negative的依赖。
此时,如果在test下创建一个test_negative表,则会使用原来的non-existent的obj#,也就是实际上不会再在obj$中插入记录,只是将原来的non-existent记录的type#从10修改为2,由于obj$记录变更,导致依赖失效,下次再访问该视图时,会重新编译解析到test.test_negative表上。
表已创建。
SYS@ ning>select obj#,owner#,name,type# from obj$ where name='TEST_NEGATIVE';
OBJ# OWNER# NAME TYPE#
---------- ---------- ------------------------------ ----------
44662 1 TEST_NEGATIVE 5
44661 42 TEST_NEGATIVE 2
44663 43 TEST_NEGATIVE 2
NING@ ning>insert into test_negative values(10);
已创建 1 行。
NING@ ning>commit;
提交完成。
TEST@ ning>select * from v_test_negative;
未选定行
参考:
http://www.jlcomp.demon.co.uk/faq/non_exist.html
http://www.ixora.com.au/q+a/library.htm