Oracle如何根據物化視圖日誌快速刷新物化視圖

轉自:http://yangtingkun.itpub.net/post/468/20584

 

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                                                NUMBER
 NAME                                              VARCHAR2(30)
 M_ROW$$                                           VARCHAR2(255)
 SEQUENCE$$                                        NUMBER
 SNAPTIME$$                                        DATE
 DMLTYPE$$                                         VARCHAR2(1)
 OLD_NEW$$                                         VARCHAR2(1)
 CHANGE_VECTOR$$                                   RAW(255)

 

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操作發生在那個或那幾個字段上。

 

有關物化視圖日誌結構的詳細描述,可以參考文檔:

 

物化視圖日誌結構:http://blog.itpub.net/post/468/20498

 

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

 

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

 

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

 

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

 

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

 

SQL> create materialized view mv_t_id refresh fast as
  2  select id, count(*) from t group by id;

 

實體化視圖已創建。

 

SQL> create materialized view mv_t_name refresh fast as
  2  select name, count(*) from t group by name;

 

實體化視圖已創建。

 

SQL> create materialized view mv_t_id_name refresh fast as
  2  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> select id, name, m_row$$, snaptime$$, dmltype$$ from mlog$_t;

 

        ID NAME       M_ROW$$            SNAPTIME$$          D
---------- ---------- ------------------ ------------------- -
         1 a          AAACJEAAFAAAAD4AAA 4000-01-01 00:00:00 I
         1 b          AAACJEAAFAAAAD4AAB 4000-01-01 00:00:00 I
         2 a          AAACJEAAFAAAAD4AAC 4000-01-01 00:00:00 I
         3 b          AAACJEAAFAAAAD4AAD 4000-01-01 00:00:00 I
         3 b          AAACJEAAFAAAAD4AAD 4000-01-01 00:00:00 U
         3 c          AAACJEAAFAAAAD4AAD 4000-01-01 00:00:00 U
         2 a          AAACJEAAFAAAAD4AAC 4000-01-01 00:00:00 D

 

已選擇7行。

 

當發生了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;

 

        ID NAME       M_ROW$$            SNAPTIME$$          D
---------- ---------- ------------------ ------------------- -
         1 a          AAACJEAAFAAAAD4AAA 2005-03-06 00:56:59 I
         1 b          AAACJEAAFAAAAD4AAB 2005-03-06 00:56:59 I
         2 a          AAACJEAAFAAAAD4AAC 2005-03-06 00:56:59 I
         3 b          AAACJEAAFAAAAD4AAD 2005-03-06 00:56:59 I
         3 b          AAACJEAAFAAAAD4AAD 2005-03-06 00:56:59 U
         3 c          AAACJEAAFAAAAD4AAD 2005-03-06 00:56:59 U
         2 a          AAACJEAAFAAAAD4AAC 2005-03-06 00:56:59 D

 

已選擇7行。

 

Oracle根據數據字典中的信息可以知道表T上建立了三個物化視圖,因此,MV_T_ID刷新完之後,不會刪除物化視圖記錄。

Oracle的數據字典中還保存着每個物化視圖上次刷新的時間和當前的刷新狀態。

SQL> select name, last_refresh from user_mview_refresh_times;

 

NAME                           LAST_REFRESH
------------------------------ -------------------
MV_T_ID                        2005-03-06 00:56:59
MV_T_ID_NAME                   2005-03-06 00:46:09
MV_T_NAME                      2005-03-06 00:46:04

 

SQL> select mview_name, last_refresh_date, staleness from user_mviews;

 

MVIEW_NAME                     LAST_REFRESH_DATE   STALENESS
------------------------------ ------------------- -------------------
MV_T_ID                        2005-03-06 00:56:59 FRESH
MV_T_ID_NAME                   2005-03-06 00:46:09 NEEDS_COMPILE
MV_T_NAME                      2005-03-06 00:46:04 NEEDS_COMPILE

 

這些視圖中記錄了每個物化視圖上次執行刷新操作的時間,並且給出每個物化視圖中的數據是否是和基表同步的。由於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;

 

        ID NAME       M_ROW$$            SNAPTIME$$          D
---------- ---------- ------------------ ------------------- -
         1 a          AAACJEAAFAAAAD4AAA 2005-03-06 00:56:59 I
         1 b          AAACJEAAFAAAAD4AAB 2005-03-06 00:56:59 I
         2 a          AAACJEAAFAAAAD4AAC 2005-03-06 00:56:59 I
         3 b          AAACJEAAFAAAAD4AAD 2005-03-06 00:56:59 I
         3 b          AAACJEAAFAAAAD4AAD 2005-03-06 00:56:59 U
         3 c          AAACJEAAFAAAAD4AAD 2005-03-06 00:56:59 U
         2 a          AAACJEAAFAAAAD4AAC 2005-03-06 00:56:59 D
         4 d          AAACJEAAFAAAAD4AAE 4000-01-01 00:00:00 I

 

已選擇8行。

 

SQL> select mview_name, last_refresh_date, staleness from user_mviews;

 

MVIEW_NAME                     LAST_REFRESH_DATE   STALENESS
------------------------------ ------------------- -------------------
MV_T_ID                        2005-03-06 00:56:59 NEEDS_COMPILE
MV_T_ID_NAME                   2005-03-06 00:46:09 NEEDS_COMPILE
MV_T_NAME                      2005-03-06 00:46:04 NEEDS_COMPILE

 

下面刷新物化視圖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;

 

        ID NAME       M_ROW$$            SNAPTIME$$          D
---------- ---------- ------------------ ------------------- -
         1 a          AAACJEAAFAAAAD4AAA 2005-03-06 00:56:59 I
         1 b          AAACJEAAFAAAAD4AAB 2005-03-06 00:56:59 I
         2 a          AAACJEAAFAAAAD4AAC 2005-03-06 00:56:59 I
         3 b          AAACJEAAFAAAAD4AAD 2005-03-06 00:56:59 I
         3 b          AAACJEAAFAAAAD4AAD 2005-03-06 00:56:59 U
         3 c          AAACJEAAFAAAAD4AAD 2005-03-06 00:56:59 U
         2 a          AAACJEAAFAAAAD4AAC 2005-03-06 00:56:59 D
         4 d          AAACJEAAFAAAAD4AAE 2005-03-06 01:18:22 I

 

已選擇8行。

 

SQL> select mview_name, last_refresh_date, staleness from user_mviews;

 

MVIEW_NAME                     LAST_REFRESH_DATE   STALENESS
------------------------------ ------------------- -------------------
MV_T_ID                        2005-03-06 00:56:59 NEEDS_COMPILE
MV_T_ID_NAME                   2005-03-06 01:18:22 FRESH
MV_T_NAME                      2005-03-06 00:46:04 NEEDS_COMPILE

 

如果這時再次刷新物化視圖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;

 

        ID NAME       M_ROW$$            SNAPTIME$$          D
---------- ---------- ------------------ ------------------- -
         1 a          AAACJEAAFAAAAD4AAA 2005-03-06 00:56:59 I
         1 b          AAACJEAAFAAAAD4AAB 2005-03-06 00:56:59 I
         2 a          AAACJEAAFAAAAD4AAC 2005-03-06 00:56:59 I
         3 b          AAACJEAAFAAAAD4AAD 2005-03-06 00:56:59 I
         3 b          AAACJEAAFAAAAD4AAD 2005-03-06 00:56:59 U
         3 c          AAACJEAAFAAAAD4AAD 2005-03-06 00:56:59 U
         2 a          AAACJEAAFAAAAD4AAC 2005-03-06 00:56:59 D
         4 d          AAACJEAAFAAAAD4AAE 2005-03-06 01:18:22 I

 

已選擇8行。

 

SQL> select mview_name, last_refresh_date, staleness from user_mviews;

 

MVIEW_NAME                     LAST_REFRESH_DATE   STALENESS
------------------------------ ------------------- -------------------
MV_T_ID                        2005-03-06 01:25:30 FRESH
MV_T_ID_NAME                   2005-03-06 01:18:22 FRESH
MV_T_NAME                      2005-03-06 00:46:04 NEEDS_COMPILE

 

到目前爲止,還沒有看到過物化視圖日誌的清除,其實每次進行完刷新,物化視圖日誌都會試圖刪除沒有用的物化視圖日誌記錄。物化視圖日誌記錄的刪除條件是刪除那些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;

 

        ID NAME       M_ROW$$            SNAPTIME$$          D
---------- ---------- ------------------ ------------------- -
         5 e          AAACJEAAFAAAAD4AAF 2005-03-06 01:31:33 I

 

SQL> select mview_name, last_refresh_date, staleness from user_mviews;

 

MVIEW_NAME                     LAST_REFRESH_DATE   STALENESS
------------------------------ ------------------- -------------------
MV_T_ID                        2005-03-06 01:25:30 NEEDS_COMPILE
MV_T_ID_NAME                   2005-03-06 01:18:22 NEEDS_COMPILE
MV_T_NAME                      2005-03-06 01:31:33 FRESH

 

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

最後,簡單總結一下:

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章