行迁移 英文的意思是 Row movement
默认情况下,oracle数据块中的一行其生命周期内是不会发生移动的,即块的rowid不会发生改变. 但是在某些情景下,我们希望行的rowid可以发生变化,这时候我们就需要启动表的row movement特性。
启用row movement特性,使用如下语句:
Alter table table_name enable row movement;
通常在三种情景下,需要启用row movement。
1:分区表
当我们允许分区表的分区键是可更新的时候,如果分区键的更新导致当前修改的数据条目需要移动到其他分区,
此时就会发生rowid的改变。
SQL> create table test_jack(id number,quantity varchar2(10))
2 partition by list(quantity)
3 (
4 partition p1 values('1'),
5 partition p2 values('2'),
6 partition p3 values('3'));
Table created.
SQL> insert into test_jack values(1,'1');
1 row created.
SQL> insert into test_jack values(2,'2');
1 row created.
SQL> insert into test_jack values(3,'3');
1 row created.
SQL> insert into test_jack values(4,'1');
1 row created.
SQL> commit;
Commit complete.
SQL> select * from test_jack partition(p1);
id quantity
------------- ----------
1 1
4 1
SQL> select * from test_jack partition(p2);
id quantity
------------- ----------
2 2
SQL> update test_jack set quantity=2 where id=4;
update test_jack set quantity=2 where id=4
*
ERROR at line 1:
ORA-14402: updating partition key column would cause a partition change
SQL> alter table test_jack enable row movement;
Table altered.
SQL> update test_jack set quantity=2 where id=4;
1 row updated.
SQL> select * from test_jack partition(p1);
id quantity
------------- ----------
1 1
SQL> select * from test_jack partition(p2);
id quantity
------------- ----------
2 2
4 2
2:闪回操作
在闪回操作时,我们同样需要开启表的row movement特性。
SQL> alter table test_jack disable row movement;
Table altered.
SQL> select current_scn from v$database;
CURRENT_SCN
-----------
1633927
SQL> select count(*) from test_jack;
COUNT(*)
----------
4
SQL> delete from test_jack where quantity=1;
1 row deleted.
SQL> commit;
Commit complete.
SQL> flashback table test_jack to scn 1633927;
flashback table test_jack to scn 1633927
*
ERROR at line 1:
ORA-08189: cannot flashback the table because row movement is not enabled
SQL> alter table test_jack enable row movement;
Table altered.
SQL> flashback table test_jack to scn 1633927;
Flashback complete.
SQL> select count(*) from test_jack;
COUNT(*)
----------
4
为什么flashback table会造成rowid的改变呢?通过下面的实验来研究一下:
SQL> create table hh(id number);
Table created.
SQL> insert into hh values(1);
1 row created.
SQL> commit;
Commit complete.
SQL> alter table hh enable row movement;
Table altered.
SQL> select current_scn from v$database;
CURRENT_SCN
-----------
1635103
SQL> update table hh set id = 2;
update table hh set id = 2
*
ERROR at line 1:
ORA-00903: invalid table name
SQL> update hh set id = 2;
1 row updated.
SQL> commit;
Commit complete.
SQL> alter session set tracefile_identifier = 'ee';
Session altered.
SQL> alter session set sql_trace=true;
Session altered.
SQL> flashback table hh to scn 1635103;
Flashback complete.
通过查看trace文件,我们可以发现oracle flashback table 是通过一个临时表sys_temp_fbt来实现的。trace文件中的部分内容如下:
********************************************************************************
sqlid='dtjmzuugu6ktw'
INSERT /*+ APPEND */ into SYS_TEMP_FBT SELECT /*+ FBTSCAN FULL(S) PARALLEL(S, DEFAULT) */ :1, :2, :3, rowid, SYS_FBT_INSDEL FROM "QIANXUN"."HH" as of SCN :4 S
********************************************************************************
sqlid='bytpvbcb8zbb6'
/* Flashback Table */ DELETE /*+ BYPASS_UJVC */ FROM (SELECT /*+ ORDERED USE_NL(S) PARALLEL(S,DEFAULT) PARALLEL(T,DEFAULT) */ S.rowid FROM SYS_TEMP_FBT T, "QIANXUN"."HH" S WHERE T.rid = S.rowid and T.action = 'D' and T.object# = : 1) V
********************************************************************************
sqlid='a3h5cbfc5b6xv'
/* Flashback Table */ INSERT /*+ PARALLEL(S, DEFAULT) PARALLEL(T, DEFAULT) */ INTO "QIANXUN"."HH" SELECT /*+ USE_NL(S) ORDERED PARALLEL(S, DEFAULT) PARALLEL(T, DEFAULT) */ S.* FROM SYS_TEMP_FBT T , "QIANXUN"."HH" as of SCN :1 S WHERE T.rid = S.rowid and T.action = 'I' and T.object# = :2
由此可见,oracle是通过SYS_TEMP_FBT进行删除操作,而后进行插入操作,因此行的rowid有可能发生改变、
SQL> select * from sys_temp_fbt ;
SCHEMA OBJECT_NAME OBJECT# RID A
---------- -------------------- ---------- ------------------------------ -
hh hh 76906 AAASxqAAGAAAAC0AAA D
hh hh 76906 AAASxqAAGAAAAC0AAA I
SQL> select tname,tabtype from tab;
TNAME TABTYPE
------------------------------ -------
HH
TABLE
SYS_TEMP_FBT TABLE
TEST_JACK TABLE
SQL> ALTER TABLE test_jackS ENABLE ROW MOVEMENT;
Table altered.
SQL> FLASHBACK TABLE test_jackS TO TIMESTAMP (SYSTIMESTAMP - INTERVAL '06' minute);
Flashback complete.
SQL> SELECT * FROM test_jackS;
id quantity OFFICE_NAME
------------- ------------ ------------------------------
1 282 DENVER
2 282 DENVER TECH CTR
3 282 DENVER WEST
4 283 BROOMFIELD
如果表的行移动禁用了,为什么可以在没有行移动的情况下删除表并将表返回到没有行移动的情况下?
下面让我们演示一遍 用行迁移恢复,通过上面的例子 可能你已经有答案了。
SQL> ALTER TABLE test_jackS DISABLE ROW MOVEMENT;
Table altered.
SQL> DROP TABLE test_jackS;
Table dropped.
SQL> FLASHBACK TABLE test_jackS TO BEFORE DROP;
Flashback complete.
没错,因为drop表Oracle有另外的回收站技术恢复过来,而删除几行记录是不能 直接通过回收站恢复的。
3:回收空间
在收缩空间时,也会造成行的移动,如下
SQL> alter table hh disable row movement;
Table altered.
SQL> alter table hh shrink space;
alter table hh shrink space
*
ERROR at line 1:
ORA-10636: ROW MOVEMENT is not enabled
SQL> alter table hh enable row movement;
Table altered.
SQL> alter table hh shrink space;
Table altered.
Shrink space操作(without compact)会导致所有已经打开的游标失效,因此需要谨慎使用!