1. 程式人生 > >Oracle如何根據物化檢視日誌快速重新整理物化檢視

Oracle如何根據物化檢視日誌快速重新整理物化檢視

轉自:http://www.cnblogs.com/linjiqin/archive/2012/05/22/2513551.html

Oracle物化檢視的快速重新整理機制是通過物化檢視日誌完成的。Oracle如何通過一個物化檢視日誌就可以支援多個物化檢視的快速重新整理呢,本文簡單的描述一下重新整理的原理。

首先,看一下物化檢視的結構:
SQL> create table t(id number, name varchar2(30), num number);
表已建立。

SQL> create materialized view log on t with rowid, sequence(id, name) including new values;
實體化檢視日誌已建立。

SQL> desc mlog$_t

ID和NAME是建立物化檢視日誌時指定的基表中的列,它們記錄每次DML操作對應的ID和NAME的值。
M_ROW$$儲存基表的ROWID資訊,根據M_ROW$$中的資訊可以定位到發生DML操作的記錄。
SEQUENCE$$根據DML操作發生的順序記錄序列的編號,當重新整理時,根據SEQUENCE$$中的順序就可以和基表中的執行順序保持一致。
SNAPTIME$$列記錄了重新整理操作的時間。
DMLTYPE$$的記錄值I、U和D,表示操作是INSERT、UPDATE還是DELETE。
OLD_NEW$$表示物化檢視日誌中儲存的資訊是DML操作之前的值(舊值)還是DML操作之後的值(新值)。除了O和N這兩種型別外,對於UPDATE操作,還可能表示為U。
CHANGE_VECTOR$$記錄DML操作發生在那個或那幾個欄位上。

根據上面的描述,可以發現,當重新整理物化檢視時,只需要根據SEQUENCE$$列給出的順序,通過M_ROW$$定位到基表的記錄,如果是UPDATE操作,通過CHANGE_VECTOR$$定位到欄位,然後根據基表中的資料重複執行DML操作。

如果物化檢視日誌只針對一個物化檢視,那麼重新整理過程就是這麼簡單,還需要做的不過是在重新整理之後將物化檢視日誌清除掉。

但是,Oracle的物化檢視日誌是可以同時支援多個物化檢視的快速重新整理的,也就是說,物化檢視在重新整理時還必須判斷哪些物化檢視日誌記錄是當前物化檢視重新整理需要的,哪些是不需要的。而且,物化檢視還必須確定,在重新整理物化檢視後,物化檢視日誌中哪些記錄是需要清除的,哪些是不需要清除的。

回顧一下物化檢視日誌的結構,發現只剩下一個SHAPTIME$$列,那麼Oracle如何僅通過這一列就完成了對多個物化檢視的支援呢?下面建立一個小例子,通過例子來進行說明。

使用上文中建立的表和物化檢視日誌,下面對這個表建立三個快速重新整理的物化檢視。      

SQL> create materialized view mv_t_id refresh fast as select id, count(*) from t group by id;
實體化檢視已建立。

SQL> create materialized view mv_t_name refresh fast as select name, count(*) from t group by name;
實體化檢視已建立。

SQL> create materialized view mv_t_id_name refresh fast as select id, name, count(*) from t group by id, name;
實體化檢視已建立。

SQL> insert into t values (1, 'a', 2);
已建立 1 行。

SQL> insert into t values (1, 'b', 3);
已建立 1 行。

SQL> insert into t values (2, 'a', 5);
已建立 1 行。

SQL> insert into t values (3, 'b', 7);
已建立 1 行。

SQL> update t set name = 'c' where id = 3;
已更新 1 行。

SQL> delete t where id = 2;
已刪除 1 行。

SQL> commit;
提交完成。

SQL> select id, name, m_row$$, snaptime$$, dmltype$$ from mlog$_t;

當發生了DML操作後,物化檢視日誌中的SNAPTIME$$列保持的值是4000-01-01 00:00:00。這個值表示這條記錄還沒有被任何物化檢視重新整理過。第一個重新整理這些記錄的物化檢視會將SNAPTIME$$的值更新為物化檢視當前的重新整理時間。

SQL> exec dbms_mview.refresh('MV_T_ID');
PL/SQL 過程已成功完成。

SQL> select id, name, m_row$$, snaptime$$, dmltype$$ from mlog$_t;

Oracle根據資料字典中的資訊可以知道表T上建立了三個物化檢視,因此,MV_T_ID重新整理完之後,不會刪除物化檢視記錄。

Oracle的資料字典中還儲存著每個物化檢視上次重新整理的時間和當前的重新整理狀態。

SQL> select name, last_refresh from user_mview_refresh_times;


SQL> select mview_name, last_refresh_date, staleness from user_mviews;

這些檢視中記錄了每個物化檢視上次執行重新整理操作的時間,並且給出每個物化檢視中的資料是否是和基表同步的。由於MV_T_ID剛剛進行了重新整理,因此狀態是FRESH,而另外兩個由於在重新整理(建立)之後,基表又進行了DML操作,因此狀態為NEEDS_COMPILE。如果這時對基表進行DML操作,則MV_T_ID的狀態也會變為NEEDS_COMPILE。

SQL> insert into t values (4, 'd', 10);
已建立 1 行。

SQL> commit;
提交完成。

SQL> select id, name, m_row$$, snaptime$$, dmltype$$ from mlog$_t;

SQL> select mview_name, last_refresh_date, staleness from user_mviews;

下面重新整理物化檢視MV_T_ID_NAME,重新整理操作的判斷依據是,只重新整理SNAPTIME$$列大於當前物化檢視的LAST_REFRESH_DATE的記錄,由於物化檢視日誌中所有記錄的SNAPTIME$$的值都比物化檢視MV_T_ID_NAME上次重新整理的時間點大,因此會重新整理所有記錄。對於SNAPTIME$$列的值是4000-01-01 00:00:00的記錄,物化檢視會把SNAPTIME$$列的值更新為當前重新整理時間,對於那些已經被更新過的SNAPTIME$$列,則保持原值。

SQL> exec dbms_mview.refresh('MV_T_ID_NAME')
PL/SQL 過程已成功完成。

SQL> select id, name, m_row$$, snaptime$$, dmltype$$ from mlog$_t;

 

SQL> select mview_name, last_refresh_date, staleness from user_mviews;

如果這時再次重新整理物化檢視MV_T_ID,則只有ID=4的這條記錄的SNAPTIME$$的時間點大於MV_T_ID上次重新整理的時間點,因此,只重新整理這一條記錄,且不會改變SNAPTIME$$的值。

         
SQL> exec dbms_mview.refresh('MV_T_ID')
PL/SQL 過程已成功完成。

          
SQL> select id, name, m_row$$, snaptime$$, dmltype$$ from mlog$_t;

SQL> select mview_name, last_refresh_date, staleness from user_mviews;

到目前為止,還沒有看到過物化檢視日誌的清除,其實每次進行完重新整理,物化檢視日誌都會試圖刪除沒有用的物化檢視日誌記錄。物化檢視日誌記錄的刪除條件是刪除那些SNAPTIME$$列小於等於基表所有物化檢視的上次重新整理時間。在上面的例子中,由於MV_T_NAME一直沒有重新整理,因此它的LAST_REFRESH_DATE比物化檢視日誌中所有記錄的值都小,因此,一直沒有發生物化檢視日誌記錄清除的現象。

     
SQL> insert into t values (5, 'e', 2);
已建立 1 行。

    
SQL> commit;
提交完成。

     
SQL> exec dbms_mview.refresh('MV_T_NAME')
PL/SQL 過程已成功完成。

    
SQL> select id, name, m_row$$, snaptime$$, dmltype$$ from mlog$_t;

SQL> select mview_name, last_refresh_date, staleness from user_mviews;

物化檢視MV_T_NAME重新整理了物化檢視中的每條記錄,更新了ID=5的記錄的SNAPTIME$$時間,並清除了其它所有物化檢視日誌記錄。

SQL> drop materialized view log on t;
實體化檢視日誌已刪除。

SQL> drop materialized view mv_t_id;
實體化檢視已刪除。

SQL> drop materialized view mv_t_name;
實體化檢視已刪除。

SQL> drop materialized view mv_t_id_name;
實體化檢視已刪除。

SQL> drop table t;
表已刪除。

SQL>

最後,簡單總結一下:
物化檢視在重新整理時,會重新整理所有SNAPTIME$$大於物化檢視上次重新整理時間的記錄,並將所有是4000-01-01 00:00:00的記錄更新為當前重新整理時間。對於其他大於上次重新整理時間的記錄,只重新整理不更改。這樣,當重新整理執行完以後,資料字典中記錄當前物化檢視的上次重新整理時間為當前時刻,這保證了物化檢視日誌中目前所有的記錄都小於或等於重新整理時間。因此,每個物化檢視只要重新整理大於上次重新整理時間的記錄,且保證每次重新整理後,所有記錄的時間都小於等於上次重新整理時間,那麼無論有多少個物化檢視,就可以互不影響的使用同一個物化檢視日誌進行快速重新整理了。當物化檢視重新整理完之後,會清除那些SNAPTIME$$列小於所有物化檢視的上次重新整理時間的記錄,而這些記錄已經被所有的物化檢視都重新整理過了,儲存在物化檢視日誌中已經沒有意義了。