物化檢視日誌的維護

yangtingkun發表於2008-05-05

物化檢視日誌經常會由於物化檢視長時間沒有重新整理,或者基表的一次批次資料更改而變得很大,這會影響物化檢視的重新整理效能,因此對於這種情況需要對物化檢視日誌進行處理,降低物化檢視日誌表的高水位線。

 

 

Oracle的物化檢視的快速重新整理功能,主要是靠物化檢視日誌來實現的。

物化檢視日誌會記錄下基表所有的增、刪、改操作,而物化檢視執行完快速重新整理操作後,會從物化檢視日誌中將本物化檢視重新整理過且其他物化檢視所不需要重新整理的記錄刪除掉。如果其中一個物化檢視一直不重新整理,那麼物化檢視日誌就會變得越來越大。

還有一種情況,比如表中插入了大量的資料,或者刪除了大量的資料,或者將表中的某一列統一更新為一個值,這種操作都會在物化檢視日誌中產生大量的記錄。

而物化檢視日誌的增大必然影響物化檢視的重新整理速度。一方面,物化檢視在重新整理的時候要掃描物化檢視日誌,另一方面,物化檢視在重新整理介紹後,也要清除物化檢視日誌中的記錄,仍然要掃描物化檢視日誌,因此物化檢視日誌的大小直接會影響物化檢視快速重新整理的速度。更重要的是,物化檢視日誌的高水位一旦增長到一個很高的位置,即使以後物化檢視日誌中記錄很少,甚至沒有記錄存在,物化檢視在重新整理的時候仍然需要較長的時間。

因此,在對於物化檢視的基表進行操作時,應注意儘量更新需要更新的記錄:

SQL> CREATE TABLE T (ID NUMBER PRIMARY KEY, NAME VARCHAR2(30));

表已建立。

SQL> INSERT INTO T SELECT ROWNUM, OBJECT_NAME FROM DBA_OBJECTS;

已建立50674行。

SQL> CREATE MATERIALIZED VIEW LOG ON T;

實體化檢視日誌已建立。

用一個最簡單的例子來說明什麼叫做更新需要更新的記錄。現在需要將T表中的NAME欄位全部用大寫來表示,最簡單的寫法:

SQL> UPDATE T SET NAME = UPPER(NAME);

已更新50674行。

SQL> SELECT COUNT(*) FROM MLOG$_T;

  COUNT(*)
----------
     50674

SQL> ROLLBACK;

回退已完成。

但是這種寫法就會造成一些沒有必要更新的記錄也執行了更新操作,從而導致物化檢視日誌中記錄了很多沒有必要重新整理的記錄,這些記錄不但影響物化檢視日誌的高水位線,而且會增加物化檢視重新整理的成本。

對於物化檢視的基表,這個重新整理則應該改寫為:

SQL> UPDATE T SET NAME = UPPER(NAME) WHERE NAME != UPPER(NAME);

已更新34007行。

SQL> SELECT COUNT(*) FROM MLOG$_T;

  COUNT(*)
----------
     34007

採用這種方式就可以避免重新整理不必要的列而使得物化檢視日誌變得很大。

不過有的時候大資料量的操作無可避免,或者物化檢視日誌本身已經變得很大,已經開始影響物化檢視的重新整理效能了,那麼就只能透過維護物化檢視日誌表的方式來降低高水位線。

不應該對物化檢視日誌執行TRUNCATE TABLE操作。因為即使查詢物化檢視日誌表中不存在記錄,也無法確保在執行TRUNCATE TABLE操作之前,沒有其他會話修改物化檢視基表,從而導致新的記錄插入物化檢視日誌中。

一旦發生物化檢視日誌記錄被TRUNCATE的情況,就會導致物化檢視和物化檢視基表的資料不一致。例如:

SQL> CREATE MATERIALIZED VIEW MV_T REFRESH FAST AS SELECT * FROM T;

實體化檢視已建立。

SQL> INSERT INTO T VALUES (60000, 'A');

已建立 1 行。

SQL> TRUNCATE TABLE MLOG$_T;

表被截斷。

SQL> INSERT INTO T VALUES (60001, 'B');

已建立 1 行。

SQL> EXEC DBMS_MVIEW.REFRESH('MV_T')

PL/SQL 過程已成功完成。

SQL> SELECT * FROM MV_T WHERE ID >= 60000;

        ID NAME
---------- ------------------------------
     60001 B

即使採用LOCK表的方式配合TRUNCATE,也無法避免併發的問題。

嘗試在TRUNCATE之前LOCK物化檢視日誌表,很可能在TRUNCATE操作的時候失敗:

SQL> LOCK TABLE MLOG$_T IN EXCLUSIVE MODE;

表已鎖定。

會話1鎖定物化檢視日誌表,這時會話2插入基表一條記錄:

SQL> SET SQLP 'SQL2> '
SQL2> INSERT INTO T VALUES (60002, 'C');

會話1執行TRUNCATE語句:

SQL> TRUNCATE TABLE MLOG$_T;
TRUNCATE TABLE MLOG$_T
               *
1 行出現錯誤:
ORA-00054:
資源正忙, 但指定以 NOWAIT 方式獲取資源

會話2成功插入記錄:


已建立 1 行。

SQL2> SELECT ID FROM MLOG$_T;

        ID
----------
     60002

這是由於會話1執行TRUNCATE操作,會先發出一個COMMIT,從而釋放了MLOG$_T上的鎖,而這時會話2獲得了MLOG$_T上的鎖,並插入記錄。由於會話2獲得了物化檢視日誌上的鎖,會話1嘗試TRUNCATE就會失敗。

如果嘗試在基表上加鎖,雖然可以避免基表的修改造成的物化檢視日誌改變,但是無法避免手工修改物化檢視日誌表的情況,雖然這種情況基本上不會發生。

因此處理物化檢視高水位線最穩妥的方法還是使用MOVE的方式。

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4227/viewspace-260459/,如需轉載,請註明出處,否則將追究法律責任。

相關文章