Oracle有很多的知识细节,是需要我们平时敏于发现、勤于思考、善于积累的。这些知识看似零碎,但是是我们学习的一个重要方面。本篇权当记录两个有意思的实验。
1、远程对象依赖关系
在笔者之前的《从视图看Oracle对象依赖机制》(http://space.itpub.net/17203031/viewspace-718906)中,比较详细的介绍了Oracle中对象依赖的基本原理和启用失效机理。简单的说,Oracle内部会维护依赖对象关系,我们可以通过dba_dependencies查看到基本的依赖关系。当依赖链条上出现失效关系,连带的状态变化会连锁变化。
但是,一个问题出现了。连带关系是在一个数据库的内部进行。如果一个本地对象依赖的是通过dblink关系连到远程的一个对象,形成的依赖关系。这样还成立吗?
如果成立,也就是说如果远程对象变为invalid或者不存在,要通知到本地数据库知晓。个人感觉太神奇了些,做个实验去证明一下。
我们选择11g实验数据库。
SQL> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
PL/SQL Release 11.2.0.1.0 - Production
CORE 11.2.0.1.0 Production
SQL> select dbid, name from v$database;
DBID NAME
---------- ---------
2351142467 NBSTEST
SQL> show user;
User is "nbs"
我们首先在nbstest上,使用nbs用户登录。之后创建实验数据表t。
SQL> create table t as select * from dba_objects where wner='NBS';
Table created
SQL> select count(*) from t;
COUNT(*)
----------
262
在另一个数据库nbsdev上,我们使用dblink连接到nbstest上。
SQL> select dbid, name from v$database;
DBID NAME
---------- ---------
3162889288 NBSDEV
SQL> select object_name, object_type, status from dba_objects where object_name='NBS_TEST';
OBJECT_NAME OBJECT_TYPE STATUS
-------------------- ------------------- -------
NBS_TEST DATABASE LINK VALID
-- Create database link
create public database link NBS_TEST
connect to NBS
using 'nbstest';
在nbsdev上创建视图,访问nbstest上的数据表t。
SQL> select count(*) from t@nbs_test;
COUNT(*)
----------
262
SQL> create view v_t as select * from t@nbs_test;
View created
SQL> select count(*) from v_t;
COUNT(*)
----------
262
SQL> select count(*) from dba_dependencies where name='V_T';
COUNT(*)
----------
0
SQL> select object_name, object_type, status from dba_objects where object_name='V_T';
OBJECT_NAME OBJECT_TYPE STATUS
-------------------- ------------------- -------
V_T VIEW VALID
注意上面的现象:我们建立了视图v_t,里面引用了nbstest上的数据表t,而且是通过本地的dblink。但是,我们没有在依赖关系中找到v_t对其他对象的任何关系依赖。这个与我们想法开始出现差异。
下面我们回来将t失效。
SQL> conn nbs/xxx@nbstest;
Connected to Oracle Database 11g Enterprise Edition Release 11.2.0.1.0
Connected as nbs
SQL> drop table t;
Table dropped
但是,nbstest上的v_t依赖状态不变,依然是valid状态。
SQL> select dbid, name from v$database;
DBID NAME
---------- ---------
3162889288 NBSDEV
SQL> select object_name, object_type, status from dba_objects where object_name='V_T';
OBJECT_NAME OBJECT_TYPE STATUS
-------------------- ------------------- -------
V_T VIEW VALID
--确实是失效了;
SQL> select count(*) from v_t;
select count(*) from v_t
ORA-00942: 表或视图不存在
ORA-02063: 紧接着 line (起自 NBS_TEST)
SQL> select object_name, object_type, status from dba_objects where object_name='V_T';
OBJECT_NAME OBJECT_TYPE STATUS
-------------------- ------------------- -------
V_T VIEW VALID
即使我们将t删除,即使我们重新使用了v_t而且报错,v_t的状态依然是valid。结论:oracle对象依赖关系对远程对象依赖关系,是一个“松连接”。依赖关系是不会连带影响到远程对象的有效性的。
那么,对于本地的dblink对象,Oracle是否会保证呢?我们继续进行实验。
--恢复状态
SQL> select object_name, object_type, status from dba_objects where object_name='V_T';
OBJECT_NAME OBJECT_TYPE STATUS
-------------------- ------------------- -------
V_T VIEW VALID
SQL> select count(*) from v_t;
COUNT(*)
----------
262
--最后一的关系对象dblink
SQL> drop public database link NBS_TEST;
Database link dropped
SQL> select object_name, object_type, status from dba_objects where object_name='V_T';
OBJECT_NAME OBJECT_TYPE STATUS
-------------------- ------------------- -------
V_T VIEW VALID
结论:看来Oracle对于涉及到远程连接的内容,都是不纳入到依赖关系列表的。
2、Truncate回收空间失效
这个问题是源自QQ群一个朋友的问题。我们都知道truncate table是一个典型的ddl语句。对一个数据表进行truncate操作,意味着数据快速删除、空间快速回收、高水位线降低。通常情况下,我们进行truncate操作后,数据表会恢复到一个extent大小。注意:truncate操作是不会影响到头块地址的。如下面的简单实验:
SQL> create table t as select * from dba_objects;
Table created
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);
PL/SQL procedure successfully completed
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS, INITIAL_EXTENT, MIN_EXTENTS from dba_segments where wner='SYS' and segment_name='T';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS INITIAL_EXTENT MIN_EXTENTS
----------- ------------ ---------- ---------- ---------- -------------- -----------
1 63081 6291456 768 21 65536 1
此时,Oracle分配21个分区,768个数据块。进行truncate操作。
SQL> truncate table t;
Table truncated
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS, INITIAL_EXTENT, MIN_EXTENTS from dba_segments where wner='SYS' and segment_name='T';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS INITIAL_EXTENT MIN_EXTENTS
----------- ------------ ---------- ---------- ---------- -------------- -----------
1 63081 65536 8 1 65536 1
当truncate之后,数据段恢复到1个分区大小。
那么,是不是所有的情况都是如此呢?QQ提问的那个朋友就遇到了这样的情况。
--恢复空间
SQL> select count(*) from t_ba;
COUNT(*)
----------
50688
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS from dba_segments where wner='SYS' and segment_name='T_BA';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS
----------- ------------ ---------- ----------
1 64137 20971520 2560
--Truncate Table 操作
SQL> truncate table t_ba;
Table truncated
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS,extents from dba_segments where wner='SYS' and segment_name='T_BA';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS
----------- ------------ ---------- ---------- ----------
1 64137 20971520 2560 20
SQL> select count(*) from t_ba;
COUNT(*)
----------
0
根据字典信息,数据表T_BA大小没有变化,空间没有回收,高水位线没有完全下降到一个分区大小。但是数据已经没有了。
按照我们的经验,truncate意味着空间的快速回收,应该不会保留如此多的数据extents。问题出现在哪里呢?我们检查一下数据字典。
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS, INITIAL_EXTENT, MIN_EXTENTS from dba_segments where wner='SYS' and segment_name='T_BA';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS INITIAL_EXTENT MIN_EXTENTS
----------- ------------ ---------- ---------- ---------- -------------- -----------
1 64137 20971520 2560 20 19988480 1
注意init_extents取值,19988480不是我们常见的数据表初始化取值。的确笔者在此处进行了处理,下面是处理语句。
SQL> alter table t storage (minextents 20);
alter table t storage (minextents 20)
ORA-25150: 不允许对区参数执行 ALTERING
SQL> create table t_ba storage (minextents 20) as select * from t;
Table created
我们在创建数据表的时候,storage子句集合中minextents参数一般是选择默认值1,也就是我们经常truncate回缩的大小。但是如果我们在建表(也只能在建表时),指定一个不同的取值,那么数据段就会保持这个大小。即使在使用truncate操作。
下一个问题是,我们一般在创建数据表的时候,是不会轻易设置这个取值的。那么为什么我们的系统中还会出现这种情况呢?我们说,这个是由于一些脚本抽取、数据备份工具的副作用引起的。
当我们使用一些备份还原工具的时候,在提取数据DDL阶段,会将当前数据段大小作为初始段空间分配。这样,导入数据所需要段空间就是创建段的时候被分配好了,这样进行数据插入的时候,可以避免频繁的进行空间分配操作。应该说,这是一种优化策略。
但是,这样的数据表在truncate或者其他一些空间收缩操作的时候,就遇到了麻烦,就是始终是一个很大的数据段。
了解了原因,问题就很好解决。可以通过数据表重构、空间重新分配等方式进行调节。但是,从笔者的角度出发,认为调节操作应慎重:
ü 确定进行空间回收的目的。膨大的init_extents起码表示曾经该数据表达到过某一个大小。正常业务场景下,数据量可能就是维持一个稳定值。如果只是因为当前我们暂时性的表被清空,或者数据减少,过一段时间后还会回到原有大小。那么,进行调整的意义真不是很大;
ü 如果确定需要暂时性的回收空间,或者业务确实发生变化,可以进行这个操作。
3、结论
上述两个小知识点,权当拾遗。记录下来,供有需要的朋友应急。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/17203031/viewspace-721987/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/17203031/viewspace-721987/