Oracle數據庫入門——如何根據物化視圖日誌快速刷新物化視圖

Oracle物化視圖的快速刷新機制是經過物化視圖日誌完成的。Oracle如何經過一個物化視圖日誌就能夠支持多個物化視圖的快速刷新呢,本文簡單的描述一下刷新的原理。post

         

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

           

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

                

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

                 

有關物化視圖日誌結構的詳細描述,能夠參考文檔:物化視圖日誌結構:http://blog.itpub.net/post/468/20498文檔

        

根據上面的描述,能夠發現,當刷新物化視圖時,只須要根據SEQUENCE$$列給出的順序,經過M_ROW$$定位到基表的記錄,若是是UPDATE操做,經過CHANGE_VECTOR$$定位到字段,而後根據基表中的數據重複執行DML操做。get

        

若是物化視圖日誌只針對一個物化視圖,那麼刷新過程就是這麼簡單,還須要作的不過是在刷新以後將物化視圖日誌清除掉。同步

              

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

                 

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

            

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

          

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$$列小於全部物化視圖的上次刷新時間的記錄,而這些記錄已經被全部的物化視圖都刷新過了,保存在物化視圖日誌中已經沒有意義了。

相關文章
相關標籤/搜索