在move操作的时候,进行的是block之间的数据copy,所以table所位于的block的区域会发生变化;所有行物理存储的顺序不会发生变化,但行号会变成按AAA增1的顺子,所以block里行会挪紧,实现了消除HWM和行碎片(包括行迁移问题),但不会释放申请的空间(其他segment不能使用此部分的空闲空间,只能本segment增加数据时使用),得到了空闲空间但不释放,更说明是block之间的copy,只是block内行会挪紧。当前的tablespace中需要有1倍于table的空闲空间以供使用。所有行的rowid都发生了变化,index是通过rowid来fetch数据行的,所以,table上的index是必须要rebuild的。table在进行move操作时,table上加了exclusive lock,我们只能对它进行select的操作。反过来说,当我们的一个session对table进行DML操作且没有commit时,在另一个session中是不能对这个table进行move操作的,否则oracle会返回这样的错误信息:ORA-00054: 资源正忙,要求指定 NOWAIT。最后重新编译数据库所有失效的对象,收集新的table统计分析数据。
实验:
1.建表并插入数据:
SQL> create table sjh.test1(id int) tablespace users;
表已创建。
SQL> insert into sjh.test1 values(1);
已创建 1 行。
SQL> insert into sjh.test1 values(2);
已创建 1 行。
SQL> insert into sjh.test1 values(3);
已创建 1 行。
SQL> insert into sjh.test1 values(4);
已创建 1 行。
SQL> insert into sjh.test1 values(5);
已创建 1 行。
SQL> insert into sjh.test1 values(6);
已创建 1 行。
SQL> insert into sjh.test1 values(7);
已创建 1 行。
SQL> insert into sjh.test1 values(8);
已创建 1 行。
SQL> commit;
提交完成。
SQL> select * from sjh.test1;
ID
----------
1
2
3
4
5
6
7
8
已选择8行。
2.查看表的rowid信息和block id信息:
SQL> select rowid,id from sjh.test1;
ROWID ID
------------------ ----------
AAAMlQAAEAAAABHAAA 1
AAAMlQAAEAAAABHAAB 2
AAAMlQAAEAAAABHAAC 3
AAAMlQAAEAAAABHAAD 4
AAAMlQAAEAAAABHAAE 5
AAAMlQAAEAAAABHAAF 6
AAAMlQAAEAAAABHAAG 7
AAAMlQAAEAAAABHAAH 8
已选择8行。
SQL> select EXTENT_ID,FILE_ID,RELATIVE_FNO,BLOCK_ID,BLOCKS
2 from dba_extents where segment_name='TEST1';
EXTENT_ID FILE_ID RELATIVE_FNO BLOCK_ID BLOCKS
---------- ---------- ------------ ---------- ----------
0 4 4 65 8
--8条记录都在一个块上(AAAABH)
这里简单介绍一下ROWID的知识:ROWID 在磁盘上需要10个字节的存储空间并使用18个字符来显示它包含下列组件:
数据对象编号:每个数据对象如表或索引在创建时都分配有此编号,并且此编号在数据库中是唯一的;
相关文件编号:此编号对于一个表空间中的每个文件是唯一的;
块编号:表示包含此行的块在文件中的位置;
行编号:标识块头中行目录位置的位置;
在内部数据对象编号需要32 位,相关文件编号需要10 位,块编号需要22,位行编号需要16 位,加起来总共是80 位或10 个字节,ROWID 使用以64 为基数的编码方案来显示该方案将六个位置用于数据对象,编号三个位置用于相关文件编号六个位置用于块编号三个位置用于行编号以64 为基数的编码方案使用字符A-Z a-z 0-9 + 和/共64 个字符。
如下例所示:AAAMlQ AAE AAAABH AAA
在本例中
AAAMlQ 是数据对象编号
AAE 是相关文件编号
AAAABH 是块编号
AAA 是行编号
3.做一些DML操作,再观察ROWID有没有发生变化:
SQL> delete from sjh.test1 where id=1;
已删除 1 行。
SQL> delete from sjh.test1 where id=3;
已删除 1 行。
SQL> delete from sjh.test1 where id=5;
已删除 1 行。
SQL> commit;
提交完成。
SQL> select rowid,id from sjh.test1;
ROWID ID
------------------ ----------
AAAMlQAAEAAAABHAAB 2
AAAMlQAAEAAAABHAAD 4
AAAMlQAAEAAAABHAAF 6
AAAMlQAAEAAAABHAAG 7
AAAMlQAAEAAAABHAAH 8
--我们看到ROWID保持不变。
4.做MOVE操作,然后观察ROWID的情况:
SQL> alter table sjh.test1 move;
表已更改。
SQL> select rowid,id from sjh.test1;
ROWID ID
------------------ ----------
AAAMlRAAEAAAABMAAA 2
AAAMlRAAEAAAABMAAB 4
AAAMlRAAEAAAABMAAC 6
AAAMlRAAEAAAABMAAD 7
AAAMlRAAEAAAABMAAE 8
SQL> select EXTENT_ID,FILE_ID,RELATIVE_FNO,BLOCK_ID,BLOCKS
2 from dba_extents where segment_name='TEST1';
EXTENT_ID FILE_ID RELATIVE_FNO BLOCK_ID BLOCKS
---------- ---------- ------------ ---------- ----------
0 4 4 73 8
--ROWID发生变化BLOCK_ID由原来的65变为73,BLOCK的编号由原来的AAAABH变为AAAABM
5.move对HWM的影响:
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降低了!(show_space是自定义的一个过程)。
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);
c.另外,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 ;
SQL> create table my_objects tablespace test1 as select * from all_objects;
表已创建。
SQL> select bytes/1024/1024 from user_segments where segment_name='MY_OBJECTS';
BYTES/1024/1024
---------------
3.125
SQL> alter table MY_OBJECTS move;
alter table MY_OBJECTS move
*
ERROR 位于第 1 行:
ORA-01652: 无法通过16(在表空间TEST1中)扩展 temp 段
SQL> ALTER DATABASE
2 DATAFILE 'D:\ORACLE\ORADATA\ORACLE9I\TEST1.DBF' RESIZE 7M;
数据库已更改。
SQL> alter table MY_OBJECTS move;
表已更改。