ORACLE系統包DBMS_REDEFINITION實現表的在線重定義

        ORACLE自帶的DBMS_REDEFINITION包功能非常強大,可以用於降低高水位線,回收碎片空間,對錶做在線重定義(添加或刪除字段,改變字段類型,普通表重定義表爲分區表,分區表重定義表爲普通表等等)

        另外,它有一個非常強大的功能,“在線”二字體現得淋漓盡致,但是也並不是完全的在線,因爲在完成重定義前的最後一下,會持會表級排他鎖,但這個鎖定時間是可控的。

        如果你也曾爲delete無法降低高水位線而煩惱或者由於歷史表太大導致維護困難,我相信這篇文章將對你有很大的感觸,因此相信絕大朋友都用過諸如exp/imp,EXPDP/IMPDP,SHINK SPACE,MOVE等釋放碎片,但業務影響範圍非常大,相比之下DBMS_REDEFINITION的方式就有太多的優勢了!

 

以下,拿普通表在線重定義成分區表爲例,演示歷史表過大導致維護困難:

 

SQL> list
  1* create table ori_tab tablespace users as select * from ab
SQL> select count(*) from ori_tab;

  COUNT(*)
----------
       100

SQL> SQL> ed
Wrote file afiedt.buf

  1  begin
  2    dbms_stats.gather_table_stats( 'T1','ORI_TAB' );
  3* end;
SQL> /

PL/SQL procedure successfully completed.
SQL> select blocks,empty_blocks from user_tables where table_name='ORI_TAB';

    BLOCKS EMPTY_BLOCKS
---------- ------------
  4       0               <<<<佔用4個數據塊

SQL>  delete from ori_tab where a > 50;

50 rows deleted.

SQL> COMMIT;

Commit complete.

SQL> select count(distinct dbms_rowid.rowid_block_number(rowid)) from ori_tab;

COUNT(DISTINCTDBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID))
---------------------------------------------------
        1        <<<<<<<<<實際數據只佔用1個數據塊,說明delete操作並沒有回收高水位線

SQL> alter table ori_tab add constraint oritab_a_pk primary key (a);    ---爲表創建主鍵,爲了在線重定做準備(重定義可以有兩種方式rowid或主鍵_not NULL)

Table altered.


+++1檢查ORI_TAB是否支持在線重定義
SQL> exec dbms_redefinition.can_redef_table(uname => 'T1',tname => 'ORI_TAB');

PL/SQL procedure successfully completed.

沒有報錯,說明當前表支持在線重定義

+++2創建新表INT_TAB
SQL> ed
Wrote file afiedt.buf

  1  CREATE TABLE int_tab
  2  ( a number
  3  , b varchar2(2)
  4  )
  5    PARTITION BY RANGE (a)
  6  ( PARTITION p0 VALUES LESS THAN (10),
  7    PARTITION p1 VALUES LESS THAN (20),
  8    PARTITION p2 VALUES LESS THAN (30),
  9  PARTITION p3 VALUES LESS THAN (40),
 10*  PARTITION p4 values less than (maxvalue))
SQL> /

Table created.


+++3 開始做
SQL> alter session force parallel dml parallel 4;

Session altered.

SQL> alter session force parallel query parallel 4;

Session altered.

SQL> exec dbms_redefinition.start_redef_table(uname => 'T1',orig_table => 'ORI_TAB',int_table => 'INT_TAB',col_mapping => NULL,options_flag => DBMS_REDEFINITION.CONS_USE_PK);

PL/SQL procedure successfully completed.

SQL> SELECT COUNT(*) FROM INT_TAB;

  COUNT(*)
----------
 50       <<<<<<<已經看到數據被複制過去了

SQL> select index_name from user_indexes where table_name='INT_TAB';

no rows selected               <<<<<<<<<<<<索引還未自動創建

SQL> change/INT_TAB/ORI_TAB
  1* select index_name from user_indexes where table_name='ORI_TAB'
SQL> /

INDEX_NAME
--------------------------------------------------------------------------------
ORITAB_A_PK

+++4 COPY相關對象(索引,觸發器,視圖等)

SQL> INSERT INTO ORI_TAB VALUES(51,'NW');

1 row created.

SQL> COMMIT;

Commit complete.

SQL> SELECT COUNT(*) FROM ORI_TAB;

  COUNT(*)
----------
 51              <<<<源本是51條記錄

SQL> SELECT COUNT(*) FROM INT_TAB;

  COUNT(*)
----------
 50              <<<<新表依舊是50條記錄

SQL> ed
Wrote file afiedt.buf

  1  DECLARE
  2    num_errors PLS_INTEGER;
  3  BEGIN
  4    DBMS_REDEFINITION.COPY_TABLE_DEPENDENTS('T1',
  5            'ORI_TAB',
  6            'INT_TAB',
  7            DBMS_REDEFINITION.CONS_ORIG_PARAMS,
  8            TRUE,
  9            TRUE,
 10            TRUE,
 11            TRUE,
 12            num_errors);
 13* END;
SQL> /

PL/SQL procedure successfully completed.

SQL> SELECT COUNT(*) FROM ORI_TAB;

  COUNT(*)
----------
 51

SQL> SELECT COUNT(*) FROM INT_TAB;

  COUNT(*)
----------
 50

SQL> select index_name from user_indexes where table_name='INT_TAB';

INDEX_NAME
--------------------------------------------------------------------------------
TMP$$_ORITAB_A_PK0                   <<<<索引已經被自動創建


+++5 源表與新表做同步
SQL> exec dbms_redefinition.sync_interim_table('T1','ORI_TAB','INT_TAB');

PL/SQL procedure successfully completed.

SQL> SELECT COUNT(*) FROM ORI_TAB;

  COUNT(*)
----------
 51

SQL> SELECT COUNT(*) FROM INT_TAB;

  COUNT(*)
----------
 51                 <<<<<說明如果源表被更新頻繁,我們也可以通過該方法使源表與新表儘可能多地進行數據同步
 
 
注:SYNC的程序決定了下面一步FINSH的時間長短,因爲在線重定義方法在最後一步會對錶持有表級排他鎖。

+++6 完成在線重定義
SQL> exec dbms_redefinition.finish_redef_table('T1','ORI_TAB','INT_TAB');

PL/SQL procedure successfully completed.

另一個窗口查詢源表ORI_TAB:
SQL> /

  COUNT(*)
----------
 51

SQL> /
/
/                 <<<<<<<此時持有表級排他鎖

  COUNT(*)
----------
 51

SQL>
  COUNT(*)
----------
 51

SQL>
  COUNT(*)
----------
 51

 

至此,表的在線重定義就算完成了,原理就是通過一個臨時段對舊數據進行同步,最終將舊數據段切換爲生產。因此該方法對可用空間有要求,需要額外的空間進行數據同步並保存。

 

-------------------------------------------------------------------------------------------------

本文來自於我的技術博客 http://blog.csdn.net/robo23

轉載請標註源文鏈接,否則追究法律責任!

發佈了50 篇原創文章 · 獲贊 2 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章