从8i开始,oracle开始提供Move的命令。我们通常使用这个命令,将一个table segment从一个tablespace移动到另一个tablespace。
Move实际上是在block之间物理的copy数据,那么,我们可以通过这种方式来降低table的HWM。我们先通过一个实验来看看move是如何移动数据的。创建table TEST_HWM,insert一些数据:
SQL> create table TEST_HWM (id int ,name char(2000)) tablespace hwm;
Table created
|
我们往table TEST_HWM 中insert如下数据:
insert into TEST_HWM values (1,'aa');
insert into TEST_HWM values (2,'bb');
insert into TEST_HWM values (2,'cc');
insert into TEST_HWM values (3,'dd');
insert into TEST_HWM values (4,'ds');
insert into TEST_HWM values (5,'dss');
insert into TEST_HWM values (6,'dss');
insert into TEST_HWM values (7,'ess');
insert into TEST_HWM values (8,'es');
insert into TEST_HWM values (9,'es');
insert into TEST_HWM values (10,'es');
我们来看看这个table的rowid和block的ID和信息:
SQL> select rowid , id,name from TEST_HWM;
ROWID ID NAME ------------------------- ----------- ------ AAAH7JAALAAAAAUAAA 1 aa AAAH7JAALAAAAAUAAB 2 bb AAAH7JAALAAAAAUAAC 2 cc AAAH7JAALAAAAAVAAA 3 dd AAAH7JAALAAAAAVAAB 4 ds AAAH7JAALAAAAAVAAC 5 dss AAAH7JAALAAAAAWAAA 6 dss AAAH7JAALAAAAAWAAB 7 ess AAAH7JAALAAAAAWAAC 8 es AAAH7JAALAAAAAXAAA 9 es AAAH7JAALAAAAAXAAB 10 es SQL> select EXTENT_ID,FILE_ID,RELATIVE_FNO,BLOCK_ID,BLOCKS 2 from dba_extents where segment_name='TEST_HWM' ;
EXTENT_ID FILE_ID RELATIVE_FNO BLOCK_ID BLOCKS ---------- ---------- ------------ ---------- ---------- 0 11 11 19 5 |
这里,简单地介绍一下rowid的相关知识:
ROWID 在磁盘上需要10 个字节的存储空间并使用18 个字符来显示它包含下列组件:
数据对象编号:每个数据对象如表或索引在创建时都分配有此编号,并且此编号在数据库中是唯一的;
相关文件编号:此编号对于一个表空间中的每个文件是唯一的;
块编号:表示包含此行的块在文件中的位置;
行编号:标识块头中行目录位置的位置;
在内部数据对象编号需要32 位,相关文件编号需要10 位,块编号需要22,位行编号需要16 位,加起来总共是80 位或10 个字节,ROWID 使用以64 为基数的编码方案来显示该方案将六个位置用于数据对象,编号三个位置用于相关文件编号六个位置用于块编号三个位置用于行编号以64 为基数的编码方案使用字符A-Z a-z 0-9 + 和/共64 个字符,如下例所示:
AAAH7J AAL AAAAAU AAA
在本例中
AAAH7J 是数据对象编号
AAL 是相关文件编号
AAAAAU 是块编号
AAA 是行编号
那么,我们根据数据的rowid,可以看出这11行数据分布在AAAAAU,AAAAAV,AAAAAW,AAAAAX这四个block中。
然后我们从table TEST_HWM中delete一些数据:
delete from TEST_HWM where id = 2;
delete from TEST_HWM where id = 4;
delete from TEST_HWM where id = 3;
delete from TEST_HWM where id = 7;
delete from TEST_HWM where id = 8;
delete from TEST_HWM where id = 9;
我们在来看看这个table的rowid和block的ID和信息:
SQL> select rowid , id,name from TEST_HWM;
ROWID ID NAME ------------------ ---------- --------- -------------- AAAH7JAALAAAAAUAAA 1 aa AAAH7JAALAAAAAVAAC 5 dss AAAH7JAALAAAAAWAAA 6 dss AAAH7JAALAAAAAXAAB 10 es SQL> select EXTENT_ID,FILE_ID,RELATIVE_FNO,BLOCK_ID,BLOCKS 2 from dba_extents where segment_name='TEST_HWM' ;
EXTENT_ID FILE_ID RELATIVE_FNO BLOCK_ID BLOCKS ---------- ---------- ------------ ---------- ---------- 0 11 11 19 5 |
在这里,我们可以看到,数据的rowid没有发生改变,我们根据数据的rowid,可以看出这4行数据依然分布在AAAAAU,AAAAAV,AAAAAW,AAAAAX这四个block中。
接下来我们对table TEST_HWM进行move的操作,然后再来观察rowid,blockid的信息:
SQL> alter table TEST_HWM move; Table altered
SQL> select rowid,id,name from HWM;
ROWID ID NAME ------------------ ---------- -------- --------------- AAAH7NAALAAAANrAAA 1 aa AAAH7NAALAAAANrAAB 5 dss AAAH7NAALAAAANrAAC 6 dss AAAH7NAALAAAANsAAA 10 es
SQL> select EXTENT_ID,FILE_ID,RELATIVE_FNO,BLOCK_ID,BLOCKS 2 from dba_extents where segment_name=' TEST_HWM ' ;
EXTENT_ID FILE_ID RELATIVE_FNO BLOCK_ID BLOCKS ---------- ---------- ------------ ---------- ---------- 0 11 11 874 5 |
我们可以看到,对table TEST_HWM进行move后,该table所在blockid发生了改变,那么数据的rowid自然也发生了改变。从上面的结果,我们可以看到,现在table TEST_HWM 的数据分布在AAAANr,AAAANs两个block中了。但是这四行数据的rowid的顺序来看,这四行数据在table中的存储顺序并没有发生改变。这也就证明了,move是在block之间物理的copy数据。
我们再来看看move操作对于table的HWM的位置有什么变化,我们同样使用系统视图all_objects来创建测试table my_objects,然后delete前9999行数据:
SQL> create table my_objects tablespace HWM 2 as select * from all_objects; SQL> delete from my_objects where rownum<10000; 9999 rows deleted SQL> select count(*) from my_objects; COUNT(*) ---------- 21015 SQL> exec show_space(p_segname => 'MY_OBJECTS',p_owner => 'DLINGER',p_type => 'TABLE'); Total Blocks............................425 Total Bytes.............................3481600 Unused Blocks...........................3 Unused Bytes............................24576 Last Used Ext FileId....................11 Last Used Ext BlockId...................1294 Last Used Block.........................2 |
这里HWM=425 - 3 + 1 = 423
然后对table MY_OBJECTS进行move操作:
SQL> alter table MY_OBJECTS move; 表已更改。 SQL> exec show_space(p_segname => 'MY_OBJECTS',p_owner => 'DLINGER',p_type => 'TABLE'); Total Blocks............................290 Total Bytes.............................2375680 Unused Blocks...........................1 Unused Bytes............................8192 Last Used Ext FileId....................11 Last Used Ext BlockId...................1584 Last Used Block.........................4 |
我们可以看到,table MY_OBJECTS的HWM从423移动到290,table的HWM降低了!
我们还可以使用别的方法来降低table的HWM,比如CTAS,insert into 等,那么move操作对redo logo的写和其他的方式比较起来是相对较少的,我们在这里就不列出把具体的实验结果了,大家有兴趣的可以自己动手来证实一下。
上面我们讨论了move的执行机制和如何使用move降低table的HWM,这里,我们补充说明move的另外一些用法,以及使用move时的一些要注意的问题。
Move的一些用法
以下是alter table 中move子句的完整语法,我们介绍其中的几点:
MOVE [ONLINE]
[segment_attributes_clause]
[data_segment_compression]
[index_org_table_clause]
[ { LOB_storage_clause | varray_col_properties }
[ { LOB_storage_clause | varray_col_properties } ]...
]
[parallel_clause]
a. 我们可以使用move将一个table从当前的tablespace上移动到另一个tablespace上,如:
alter table t move tablespace tablespace_name;
b. 我们还可以用move来改变table已有的block的存储参数,如:
alter table t move storage (initial 30k next 50k);
另外,move操作也可以用来解决table中的行迁移的问题。
使用move的一些注意事项
a. table上的index需要rebuild:
在前面我们讨论过,move操作后,数据的rowid发生了改变,我们知道,index是通过rowid来fetch数据行的,所以,table上的index是必须要rebuild的。
SQL> create index i_my_objects on my_objects (object_id); Index created
SQL> alter table my_objects move; Table altered
SQL> select index_name,status from user_indexes where index_name='I_MY_OBJECTS';
INDEX_NAME STATUS ------------------------------ -------- I_MY_OBJECTS UNUSABLE |
从这里可以看到,当table MY_OBJECTS进行move操作后,该table 上的inedx的状态为UNUSABLE,这时,我们可以使用alter index I_MY_OBJECTS rebuild online的命令,对index I_MY_OBJECTS进行在线rebuild。
b. move时对table的锁定
当我们对table MY_OBJECTS进行move操作时,查询v$locked_objects视图可以发现,table MY_OBJECTS上加了exclusive lock:
SQL>select OBJECT_ID, SESSION_ID,ORACLE_USERNAME,LOCKED_MODE from v$locked_objects;
OBJECT_ID SESSION_ID ORACLE_USERNAME LOCKED_MODE ---------- ---------- ------------------ ----------- 32471 9 DLINGER 6 SQL> select object_id from user_objects where object_name = 'MY_OBJECTS';
OBJECT_ID ---------- 32471 |
这就意味着,table在进行move操作时,我们只能对它进行select的操作。反过来说,当我们的一个session对table进行DML操作且没有commit时,在另一个session中是不能对这个table进行move操作的,否则oracle会返回这样的错误信息:ORA-00054: 资源正忙,要求指定 NOWAIT。
c. 关于move时空间使用的问题:
当我们使用alter table move来降低table的HWM时,有一点是需要注意的,这时,当前的tablespace中需要有1倍于table的空闲空间以供使用:
SQL> CREATE TABLESPACE TEST1 2 DATAFILE 'D:/ORACLE/ORADATA/ORACLE9I/TEST1.dbf' SIZE 5M 3 UNIFORM SIZE 128K ;
表已创建。
--------------- 3.125
SQL> alter table MY_OBJECTS move;
* ERROR 位于第 1 行: ORA-01652: 无法通过16(在表空间TEST1中)扩展 temp 段
2 DATAFILE 'D:/ORACLE/ORADATA/ORACLE9I/TEST1.DBF' RESIZE 7M ;
SQL> alter table MY_OBJECTS move; 表已更改。
|