天天看點

oracle線上重定義包DBMS_REDIFINITION #

<a href="http://blog.itpub.net/post/468/12855">http://blog.itpub.net/post/468/12855</a>

在一個高可用系統中,如果需要改變一個表的定義是一件比較棘手的問題,尤其是對于7×24系統。Oracle提供的基本文法基本可以滿足一般性修改,但是對于把普通堆表改為分區表,把索引組織表修改為堆表等操作就無法完成了。而且,對于被大量DML語句通路的表,幸運的是,Oracle從9i版本開始提供了線上重定義表功能,通過調用DBMS_REDEFINITION包,可以在修改表結構的同時允許DML操作。

線上重定義表具有以下功能:

1、修改表的存儲參數;

2、可以将表轉移到其他表空間;

3、增加并行查詢選項;

4、增加或删除分區;

5、重建表以減少碎片;

6、将堆表改為索引組織表或相反的操作;

7、增加或删除一個列。

調用DBMS_REDEFINITION包需要EXECUTE_CATALOG_ROLE角色,除此之外,還需要CREATE ANY TABLE、ALTER ANY TABLE、DROP ANY TABLE、LOCK

ANY TABLE和SELECT ANY TABLE的權限。

線上重定義表的步驟如下:

1.選擇一種重定義方法:

存在兩種重定義方法:一種是基于主鍵;另一種是基于ROWID。ROWID的方式不能用于索引組織表,而且重定義後會存在隐藏列M_ROW$$。預設采用主鍵的方式。

2.調用DBMS_REDEFINITION.CAN_REDEF_TABLE()過程,如果表不滿足重定義的條件,将會報錯并給出原因。

3.在用一個方案中建立一個空的中間表,根據重定義後你期望得到的結建構立中間表。比如:采用分區表,增加了COLUMN等。

4.調用DBMS_REDEFINITION.START_REDEF_TABLE()過程,并提供下列參數:被重定義的表的名稱、中間表的名稱、列的映射規則、重定義方法。

如果映射方法沒有提供,則認為所有包括在中間表中的列用于表的重定義。如果給出了映射方法,則隻考慮映射方法中給出的列。如果沒有給出重定義方法,則認為使用主鍵方式。

5.在中間表上建立觸發器、索引和限制,并進行相應的授權。任何包含中間表的完整性限制應将狀态置為disabled。

當重定義完成時,中間表上建立的觸發器、索引、限制和授權将替換重定義表上的觸發器、索引、限制和授權。中間表上disabled的限制将在重定義表上enable。

6.(可選)如果在執行DBMS_REDEFINITION.START_REDEF_TABLE()過程和執行DBMS_REDEFINITION.FINISH_REDEF_TABLE()過程直接在重定義表上執行了大量的DML操作,那麼可以選擇執行一次或多次的SYNC_INTERIM_TABLE()過程,以減少最後一步執行FINISH_REDEF_TABLE()過程時的鎖定時間。

7.執行DBMS_REDEFINITION.FINISH_REDEF_TABLE()過程完成表的重定義。這個過程中,原始表會被獨占模式鎖定一小段時間,具體時間和表的資料量有關。

執行完FINISH_REDEF_TABLE()過程後,原始表重定義後具有了中間表的屬性、索引、限制、授權和觸發器。中間表上disabled的限制在原始表上處于enabled狀态。

8.(可選)可以重命名索引、觸發器和限制。對于采用了ROWID方式重定義的表,包括了一個隐含列M_ROW$$。推薦使用下列語句經隐含列置為UNUSED狀态或删除。

ALTER TABLE TABLE_NAME SET UNUSED (M_ROW$$);

ALTER TABLE TABLE_NAME DROP UNUSED COLUMNS;

下面是進行重定義操作後的結果:

原始表根據中間表的屬性和特性進行重定義;

START_REDEF_TABLE()和FINISH_REDEF_TABLE()操作之間在中間表上建立的觸發器、索引、限制和授權,現在定義在原始表上。中間表上disabled的限制在原始表上處于enabled狀态。

原始表上定義的觸發器、索引、限制和授權建立在中間表上,并會在删除中間表時删除。原始表上原來enabled狀态的索引,建立在中間表上,并處于disabled狀态。

任何定義在原始表上的存儲過程和遊标都會變為INVALID,當下次調用時後自動進行編譯。

如果執行過程中出現錯誤或者人為選擇退出的話,可以執行DBMS_REDEFINITION.ABORT_REDEF_TABLE()過程。

<a href="http://hi.baidu.com/edeed/blog/item/ee6811dfb7e5d61b63279832.html">http://hi.baidu.com/edeed/blog/item/ee6811dfb7e5d61b63279832.html</a>

使用Oracle線上重定義包 DBMS_REDEFINITION 在不停業務的情況下增加或修改字段

2009-04-07 15:53

--原表的定義語句, 做線上重定義之前, 記得先把原表定義語句先取出來備用, 可以從toad裡取得.

ALTER TABLE U_TEST.CARD_TEST DROP PRIMARY KEY CASCADE;

DROP TABLE U_TEST.CARD_TEST CASCADE CONSTRAINTS;

CREATE TABLE U_TEST.CARD_TEST

(

CARD_NO       VARCHAR2(32 BYTE)               NOT NULL,

OPERATE_TIME DATE,

OPERATOR_ID   NUMBER(10)                      DEFAULT 0                     NOT NULL

)

TABLESPACE U_TEST_USER01;

CREATE UNIQUE INDEX U_TEST.CARD_TEST_PK ON U_TEST.CARD_TEST (CARD_NO) TABLESPACE U_TEST_USER01;

DROP SYNONYM U_TEST_APP.CARD_TEST;

CREATE SYNONYM U_TEST_APP.CARD_TEST FOR U_TEST.CARD_TEST;

ALTER TABLE U_TEST.CARD_TEST ADD CONSTRAINT CARD_TEST_PK PRIMARY KEY (CARD_NO) USING INDEX TABLESPACE U_TEST_USER01;

GRANT SELECT ON U_TEST.CARD_TEST TO U_TEST_APP;

--做一個臨時存儲過程,模拟不斷在對該表插入資料,因為線上重定義期間要求不影響原有DML操作

grant execute on SYS.DBMS_LOCK to U_TEST;

CREATE OR REPLACE PROCEDURE U_TEST.sp_test

AS

   v_temp   pls_integer := 0;

BEGIN

   FOR i IN 1 .. 1200

   LOOP

      --注意insert語句裡要有字段清單, 否則重定義期間該過程會有ORA-00947: not enough values錯誤

      INSERT INTO U_TEST.CARD_TEST (CARD_NO, OPERATE_TIME, OPERATOR_ID)

        VALUES   ('tmp' || TO_CHAR (i), SYSDATE, i);

      v_temp := v_temp + 1;

      COMMIT;

      sys.DBMS_LOCK.sleep (1 / 2);

   END LOOP;

END;

/

--下面開始重定義, 以dba使用者登陸并開始執行臨時存儲過程

SQL&gt; conn system/xxx

SQL&gt; exec U_TEST.sp_test;

-- 聯機重定義必須考慮的問題: 會占用源表兩倍的空間, 此外還要考慮物化視圖Log的空間以及帶來的其他開銷. 參考http://orafaq.com/node/4

-- 0. 确認正在往表裡插入資料

SQL&gt; select count(*) from U_TEST.CARD_TEST;

-- 1. 确認是否可重定義,如果表不滿足重定義的條件, 将會報錯并給出原因, 預設要求原表有主鍵, 否則得使用rowid标志

SQL&gt; EXEC DBMS_REDEFINITION.CAN_REDEF_TABLE('U_TEST', 'CARD_TEST');

-- 2. 建立一個空的中間表,中間表裡定義好新增字段屬性

SQL&gt; CREATE TABLE U_TEST.CARD_TEST_TMP

CARD_NO       VARCHAR2(32 BYTE) NOT NULL,

OPERATOR_ID   NUMBER(10) DEFAULT 0 NOT NULL,

GAME_ID       NUMBER(3) DEFAULT 1 NOT NULL

-- 3. 開始重定義

SQL&gt; EXEC DBMS_REDEFINITION.START_REDEF_TABLE('U_TEST', 'CARD_TEST', 'CARD_TEST_TMP');

-- 此時也可以做修改列名的操作,如:

SQL&gt; EXEC DBMS_REDEFINITION.START_REDEF_TABLE('U_TEST', 'CARD_TEST', 'CARD_TEST_TMP', 'CARD_NO CARD_NO, OPERATE_TIME OPERATE_DATE, OPERATOR_ID+100 OPERATOR_ID');

-- 4. 在中間表上建立原表就有的索引,限制,授權,觸發器語句等.

-- 在10g中, 如果這些定義變化了可以通過REGISTER_DEPENDENT_OBJECT()來建立, 否則調用COPY_TABLE_DEPENDENTS()即可.

-- 4.1 定義發生變化的情況下(比如修改了主鍵)

SQL&gt; ALTER TABLE U_TEST.CARD_TEST_TMP ADD CONSTRAINT CARD_TEST_PK1 PRIMARY KEY (CARD_NO, GAME_ID) USING INDEX TABLESPACE U_TEST_index01;

SQL&gt; exec DBMS_REDEFINITION.register_dependent_object('U_TEST','CARD_TEST','CARD_TEST_TMP',

    dep_type =&gt; DBMS_REDEFINITION.cons_constraint,

    dep_owner =&gt; 'U_TEST',

    dep_orig_name =&gt; 'CARD_TEST_PK',

    dep_int_name =&gt; 'CARD_TEST_PK1');

--4.2 定義未發生變化的情況下(預設把所有索引,限制,授權,觸發器語句都拷貝過來,不包含同義詞,同義詞會一直保留于原表)

DECLARE

   retval   NUMBER (5);

   DBMS_REDEFINITION.copy_table_dependents ('U_TEST',

                                            'CARD_TEST',

                                            'CARD_TEST_TMP',

                                            ignore_errors   =&gt; TRUE,

                                            num_errors      =&gt; retval);

   DBMS_OUTPUT.put_line (retval);

-- 5. 重定義期間, 原表資料可能發生了變化, 選擇執行一次或多次的SYNC_INTERIM_TABLE()對資料同步, 以減少最後一步執行FINISH_REDEF_TABLE()過程時的鎖定時間.

SQL&gt; EXEC dbms_redefinition.sync_interim_table('U_TEST', 'CARD_TEST', 'CARD_TEST_TMP');

-- 6. 完成表的重定義, 原始表會被獨占模式鎖定一小段時間, 具體時間和表的資料量有關.

SQL&gt; EXEC DBMS_REDEFINITION.FINISH_REDEF_TABLE('U_TEST', 'CARD_TEST', 'CARD_TEST_TMP');

-- 此時如果重定義失敗, 通過ABORT_REDEF_TABLE()過程釋放快照, 然後重來

SQL&gt; EXEC DBMS_REDEFINITION.ABORT_REDEF_TABLE('U_TEST', 'CARD_TEST', 'CARD_TEST_TMP');

-- 7. 驗證充定義結果

SQL&gt; desc U_TEST.CARD_TEST;

Name                        Null?    Type

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

CARD_NO                     NOT NULL VARCHAR2(32)

OPERATE_TIME                         DATE

OPERATOR_ID                 NOT NULL NUMBER(10)

GAME_ID                     NOT NULL NUMBER(3)

-- 确認還正常在往表裡插入資料

-- 8. 删除中間表并編譯所有可能失效對象

SQL&gt; drop TABLE U_TEST.CARD_TEST_TMP;

SQL&gt; exec utl_recomp.recomp_serial();

--END--