1、何时应该降低 HWM
table中包含两种空闲的block,在HWM之上的空闲block 和 在HWM之下的空闲block。
1、在HWM之上的空闲block : 运行analyze table后,在HWM之上的空心啊block会在user_tables 的 empty_blocks中 被统计,这些空闲的blocks实际上是从来没有存储过数据的,可以用以下命令来释放这些空间:
SQL> alter table table_name deallocate unused;
下面做一个实验来验证以下:
SQL> create table my_objects3 as select * from all_objects;
表已创建。 ——创建一个新表,一会用于测试
先分析一下:
SQL> exec show_space(p_segname=>'MY_OBJECTS3' ,p_owner=> 'SYS' ,p_type=>'TABLE');
Total Blocks............................768
Total Bytes.............................6291456
Unused Blocks...........................118
Unused Bytes............................966656
Last Used Ext FileId....................1
Last Used Ext BlockId...................57225
Last Used Block.........................10
高水位线在 768-118 + 1 = 651块 处。
查询在HWM之上的空闲blocks数量:
SQL> select empty_blocks from user_tables where table_name='MY_OBJECTS2';
EMPTY_BLOCKS
------------
哎哎。。我擦,没有出现结果,然后去oracle的参考文档,查到了下面的一些话:
EMPTY_BLOCKS* |
NUMBER |
Number of empty (never used) data blocks in the table |
Note:
Columns marked with an asterisk (*
) are populated only if you collect statistics on the table with the ANALYZE
statement or the DBMS_STATS
package.(意思就是说先经过分析,才能查出数据来。)
SQL> analyze table my_objects3 compute statistics;
表已分析。
SQL> select empty_blocks from user_tables where table_name='MY_OBJECTS3';
EMPTY_BLOCKS
------------
118
这下出来了。。。有118个块没有存储过数据。。。
继续用我们今天开篇介绍的语句alter table 。。。。deallocate unused 消除没有使用的数据块。
SQL> alter table my_objects3 deallocate unused;
表已更改。
SQL> analyze table my_objects3 compute statistics;
表已分析。
SQL> select empty_blocks from user_tables where table_name='MY_OBJECTS3';
EMPTY_BLOCKS
------------
6
SQL> exec show_space(p_segname=>'MY_OBJECTS3' ,p_owner=> 'SYS' ,p_type=>'TABLE');
Total Blocks............................656 ——与之前的统计比,减少了112块。
Total Bytes.............................5373952
Unused Blocks...........................6 ——与之前统计比,减少了118-6=112块。
Unused Bytes............................49152
Last Used Ext FileId....................1
Last Used Ext BlockId...................57225
Last Used Block.........................10
PL/SQL 过程已成功完成。
高水位线在 656-6+1=651 处 ,此时,表的高水位线没有变化,其实我们完全可以理解,因为alter table 。。 deallocate unused ; 只是清除的HWM之上的块。这一点在我们开始就说明了。在此也得到了验证。
2、在HWM 之下的空闲block
当数据插入到一个block后,那么HWM就移动到这个block之上了。然后后续的操作又将这个block中的数据删除了,那么,这个block实际上是空闲的。但是这些block位于HWM之下,所以是不会出现在empty_blocks 列中的。那么这样的,在HWM之下的块过多会影响性能的,就像前面讨论过的 table full scan 中看到的那样。
下面测试:
SQL> delete from my_objects3 where rownum<15000;
已删除14999行。
SQL> exec show_space(p_segname=>'MY_OBJECTS3' ,p_owner=> 'SYS' ,p_type=>'TABLE');
Total Blocks............................656
Total Bytes.............................5373952
Unused Blocks...........................6
Unused Bytes............................49152
Last Used Ext FileId....................1
Last Used Ext BlockId...................57225
Last Used Block.........................10
PL/SQL 过程已成功完成。
可以看到删除了之后没有任何变化。这是因为删除操作不会对HWM 进行移动。
3、在assm中HWM下的blocks使用情况
运用一个脚本:
http://blog.csdn.net/changyanmanman/article/details/8209377 (第二个过程)
执行效果:
SQL> exec show_space_assm('MY_OBJECTS3','SYS');
BEGIN show_space_assm('MY_OBJECTS3','SYS'); END;
*
第 1 行出现错误:
ORA-10614: Operation not allowed on this segment ——在system表空间上不允许执行这个过程。
ORA-06512: at "SYS.DBMS_SPACE", line 159
ORA-06512: at "SYS.SHOW_SPACE_ASSM", line 28
ORA-06512: at line 1
查看报错原因,如此说来我们只能从user 表空间上重新创建一个表,然后来分析。
SQL> conn scott/liu123
已连接。
SQL> create table my as select * from all_objects ;
表已创建。
SQL> SET SERVEROUTPUT ON
SQL> exec show_space_assm('MY','SCOTT');
free space 0-25% blocks:................0
free space 25-50% blocks:...............0
free space 50-75% blocks:...............0
free space 75-100% blocks:..............0
Full Blocks:............................545
Unformatted Blocks:.....................0
PL/SQL 过程已成功完成。
可以看到这些块,全是满的块。
SQL> delete from my where rownum < 15000; ——删除一些块之后继续测试
已删除14999行。
SQL> exec show_space_assm('MY','SCOTT');
free space 0-25% blocks:................0
free space 25-50% blocks:...............0
free space 50-75% blocks:...............0
free space 75-100% blocks:..............206
Full Blocks:............................339
Unformatted Blocks:.....................0
PL/SQL 过程已成功完成。
SQL>
这下一看就知道,一些块里的数据被删除了,这样一来不满的块也就多了。。。
闲着没事,又执行了一些show_space 空间统计的过程:
SQL> exec show_space(p_segname=>'MY' ,p_owner=> 'SCOTT' ,p_type=>'TABLE');
Total Blocks............................640
Total Bytes.............................5242880
Unused Blocks...........................77
Unused Bytes............................630784
Last Used Ext FileId....................7
Last Used Ext BlockId...................264
Last Used Block.........................51
PL/SQL 过程已成功完成。
简单算了一下,总共用的块数是640-77=563 ; 但是通过show_space_assm过程算出来的块是:206+339=545 。 这两个相差18个块。目前不知道这18个块是怎么回事,如果有懂的同学请各位留个言哈。。
2、如何降低HWM
8i以前的版本中,如果需要降低segment的HWM. 可以采用两种方法,分别是 exp/imp 和 CTAS
(ctas 参照博客:http://blog.csdn.net/changyanmanman/article/details/8211253)这两种方法大家都很熟悉,不必多说,下面介绍3种降低HWM 的方法,顺便学习这三个命令。
1、move 命令
从8i开始,oracle提供了move命令。通常使用这个命令将一个table segment 从一个tablespace 移动到另一个tablespace。move实际上是在block之间物理的copy数据。
书上做了一个实验,经过alter table table_name move; 命令操作之后,表的blockid 会发生变化,也就是说会重新移动表的数据,重新拷贝 block 到新的块。很明显只要blockid变化了。。那rowid 就肯定的。。不是原来的了。。。
下面我们通过my 表删除行进行测试:
SQL> select table_name from user_tables;
TABLE_NAME
------------------------------
MY
SALGRADE
BONUS
EMP
DEPT
SQL> select count(*) from my;
COUNT(*)
----------
23954
SQL> set serveroutput on
SQL> exec show_space(p_segname=>'MY' ,p_owner=> 'SCOTT' ,p_type=>'TABLE');
Total Blocks............................640
Total Bytes.............................5242880
Unused Blocks...........................77
Unused Bytes............................630784
Last Used Ext FileId....................7
Last Used Ext BlockId...................264
Last Used Block.........................51
PL/SQL 过程已成功完成。
SQL> delete from my where rownum<1000;
已删除999行。
SQL> exec show_space(p_segname=>'MY' ,p_owner=> 'SCOTT' ,p_type=>'TABLE');
Total Blocks............................640
Total Bytes.............................5242880
Unused Blocks...........................77
Unused Bytes............................630784
Last Used Ext FileId....................7
Last Used Ext BlockId...................264
Last Used Block.........................51
PL/SQL 过程已成功完成。
SQL> alter table my move;
表已更改。
SQL> exec show_space(p_segname=>'MY' ,p_owner=> 'SCOTT' ,p_type=>'TABLE');
Total Blocks............................384
Total Bytes.............................3145728
Unused Blocks...........................46
Unused Bytes............................376832
Last Used Ext FileId....................7
Last Used Ext BlockId...................520
Last Used Block.........................82
PL/SQL 过程已成功完成。
这下可以看到HWM 下降了。
以下是alter table 中move子句的完整语法,这里介绍其中用的几点:
move [ONLINE]
[segmetn_attributes_clause]
[data_segment_compression]
[index_org_table_clause]
[{ LOB_storage | varray_col_properties}
[ { LOB_storage_clause | varray_clo_properties } ] ...
]
[parallel_clause]
可以使用move将一个table从当前的tablespace上移动到另一个tablespace上例如:
alter table t move tablespace tablespace_name;
还可以用move来改变table已有的block 的存储参数,例如:
alter table t move storage (initial 30k next 50k) ;
另外move操作也可以用来解决table上的行迁移问题。
使用move的一些注意事项
a、table上的index需要重建(rebuild): 这个自然不用说了,数据块拷贝,rowid 改变,索引自然失效。、
b、move时对table的锁定:对 my 表进行move操作的时候,查询v$locked_objects视图可以发现,my表上加了exclusive lock。
c、move时空间使用:当前的tablespace中需要1倍于table的空闲空间以供使用。
2、DBMS_REDEFINITION
这个包是从9i开始引入的,用来做table的联机重组和重定义,可以通过这种方法在线的重组table、来移动table中的数据,降低HWM,修改table的存储参数,分区等。。
这个操作要求table上有一个主键,并要求预先创建一个带有要求修改该的存储参数的table,以便保存重新组织后的数据。保存重新组织的数据的table叫临时表。他只在重新组织期间被使用,在操作完成后可以被删除。
使用DBMS_REDEFINITION 包需要如下权限:
create any table;
alter any table;
drop any table;
lock any table;
select any table;
在dbms_redefinition上执行操作。使用dbms_redefinition重组table一般是这样几个步骤:
(1)使用dbms_redefinition. can_redef_table()验证所选择的table能够被重建。
(2)创建空的临时表,确保这个临时表定义了主键。
(3)使用dbms_redefinition.start_redef_table()进行table重组。
(4)在临时表上创建触发器、索引和约束,一般来说,这些对象与源表中的是一致的,但是名称必须不同。同时要确保所有外键约束不可用。在重组结束时,所有这些对象将替换定义在源表上的对象。
(5)使用dbms_redefinition.finish_redef_table() 完成重组过程。在这期间,源表将会lock较短的时间。
(6)删除临时表。
3、shrink 命令
如果表空间时自动段空间管理的(ASSM),就可以使用这个shrink 命令来缩小段,降低HWM,需要强调的是这个命令仅对assm 管理模式的段空间有效。
下面直接上测试过程吧:
SQL> select tablespace_name,block_size,extent_management, allocation_type,segment_space_management from dba_tablespaces where tablespace_name='ASSM';
TABLESPACE_NAME BLOCK_ SIZE EXTENT_MAN LLOCATIO SEGMEN
------------------------------ ---------- ---------- --------- ------
ASSM 8192 LOCAL SYSTEM AUTO
SQL> create table my tablespace assm as select * from all_objects; ——创建测试表
表已创建。
SQL> select count(*) from my;
COUNT(*)
----------
47231
在各个块中删除一些数据行,这样可以在各个数据块中出现空的地方,块就有很多不满的了。。。
SQL> delete from my where object_name like '%c%' ;
已删除20950行。
SQL> delete from my where object_name like '%u%';
已删除4689行。
SQL> delete from my where object_name like '%a%' ;
已删除5644行。
SQL> set serveroutput on
SQL> exec show_space(p_segname=>'MY' ,p_owner=> 'SYS' ,p_type=>'TABLE');
Total Blocks............................768
Total Bytes.............................6291456
Unused Blocks...........................98
Unused Bytes............................802816
Last Used Ext FileId....................8
Last Used Ext BlockId...................776
Last Used Block.........................30
PL/SQL 过程已成功完成。
SQL> exec show_space_assm('MY','SYS');
free space 0-25% blocks:................0
free space 25-50% blocks:...............7
free space 50-75% blocks:...............12
free space 75-100% blocks:..............452
Full Blocks:............................179
Unformatted Blocks:.....................0
PL/SQL 过程已成功完成。
现在看来由于我们刚才的删除操作,有了很多的块不满了。。下面可以用shrink命令来重组了。。
在进行shrink之前,表必须支持 行移动,这个很重要的,可以用下面的命令实现:
SQL> alter table my enable row movement;
表已更改。
现在可以用以下命令来降低my表的HWM:
SQL> alter table my shrink space;
表已更改。
SQL> exec show_space(p_segname=>'MY' ,p_owner=> 'SYS' ,p_type=>'TABLE');
Total Blocks............................208
Total Bytes.............................1703936
Unused Blocks...........................1
Unused Bytes............................8192
Last Used Ext FileId....................8
Last Used Ext BlockId...................264
Last Used Block.........................79
PL/SQL 过程已成功完成。
SQL> exec show_space_assm('MY','SYS');
free space 0-25% blocks:................1
free space 25-50% blocks:...............0
free space 50-75% blocks:...............0
free space 75-100% blocks:..............1
Full Blocks:............................193
Unformatted Blocks:.....................0
PL/SQL 过程已成功完成。
可以看到HWM下降了。。。原来不满的数据块都基本变成满满的了。。。
最后一点:可以在shrink table的同时shrink这个table上的index:
alter table my shrink space cascade;
shrink命令内部的原理:
shrink命令内部的原理到底是什么样的?我们知道,在move命令操作的时候,所有的行的rowid都发生了变化,table所位于的block的区域也发生了变化,但是所有行物理存储的顺序是没有变化的,所以我们得到的结论是oracle以block为单位进行了block间的数据copy。
那么shrink后,可以发现部分行的rowid发生了变化,同时部分行的物理存储顺序也发生了变化,而table所位于的block区域却没有变化,这说明,shrink只移动了table其中的一部分行的数据来完成释放空间,而且这个过程是在table当前的使用的block中完成的。
shrink的时候oracle移动数据是以行为单位的。oracle从当前table的存储的最后一行数据开始移动,从当前table最先使用的block开始搜索空间,也就是最后一行会尽量往表块的最前面的空闲处插入。接着倒数第二行继续向前插入。。。一直重复这个过程。
shrink命令注意的问题:
a、shrink后index是否需要rebuild:在执行完成shrink收缩操作之后,通过查询语句:
select index_name,status from user_indexes where index_name = '被收缩表生的index_name';
可以发现,index 的状态位 valid , 估计shrink 在移动行数据时,也一起维护了index上相应的数据rowid信息。可以认为,这是对move操作后需要 rebuild index的改进。但是如果一个table上的index数量较多,因为维护index的成本也是较高的,所以shrink过程中用来维护index的成本也会比较高。
b、shrink时对table的锁定: 在对table shrink时,会对table进行怎样的锁定呢?当对表my 进行shrink操作时,查询v$locked_objects 视图可以发现,表 my 上加了row-X(SX)的lock:
SQL > select object_id , session_id, oracle_username,locked_mode from v$locked_objects;
OBJECT_ID SESSION_ID ORACLE_USERNAME LOCKED_MODE
-------------- -------------------- ----------------------- ---------------------
55422 153 DLINGER 3
SQL > select object_id from user_object where object_name = 'MY_OBJECTS';
OBJECT_ID
-----------------
55422
可以看出,当table进行shrink时,对table是可以进行DML操作的。
3、shrink对空间的要求:shrink不会像move一样,它不需要额外的空闲空间。
3、其他可以移动HWM 的操作
1、insert append
但是用 /* +insert append */ into 向一个table中插入数据时,oracle不会在HWM以下寻找空间,而是直接移动HWM, 从 EMPTY_BLOCKS 中获得要使用block空间,来满足这一操作对blocks的需要。
实验测试表中,我们建立了all_objects 的映像表,然后删除掉里面的全部数据, 再向表中 insert into (注意这个顺序) 1000条数据,重新分析表,发现HWM没有移动,还是在建表时的位置。
继续delete所有数据,重新用insert /*+append*/ into 语句插入数据,执行show_space过程分析,发现HWM向后移动了。而不会在前面空的块中插入数据。
下面我们比较一下insert into 和 insert /*+append* / into 的效率问题:
经过建空表,插入测试发现 insert into 会比 insert /*+ append*/ into 慢很多,这是为什么呢?
最后提一下,在使用 insert /*+ append*/ into 时,oracle会生成表级独占锁(对表加上 exclusive 模式的TM 锁),可以阻断其他会话的DML操作。