【物化檢視】根據物化檢視日誌快速重新整理物化檢視的過程

楊奇龍發表於2011-04-12
先來再次分析一下物化檢視日誌的結構。
yang@rac1>create table t (id number ,name varchar2(30),val number);
Table created.
yang@rac1>create materialized view log on t with rowid,sequence (id,name) including  new values;
Materialized view log created.
yang@rac1>desc mlog$_t
 Name                Null?      Type
 ------------------------------ -------
 ID                  NUMBER
 NAME                VARCHAR2(30)
 M_ROW$$             VARCHAR2(255)
 SEQUENCE$$          NUMBER
 SNAPTIME$$          DATE
 DMLTYPE$$           VARCHAR2(1)
 OLD_NEW$$           VARCHAR2(1)
 CHANGE_VECTOR$$     RAW(255)
 XID$$               NUMBER
ID,NAME 記錄表中的欄位的值                
M_ROW$$  記錄變更欄位所在列的rowid
SEQUENCE$$ 當某一行資料多次發生變更,記錄變更的順序。
SNAPTIME$$ --這篇文章將要講述的欄位
DMLTYPE$$  
OLD_NEW$$           
CHANGE_VECTOR$$ --變更向量。定位變更的欄位位置。    
XID$$              

從物化檢視日誌的結構可以得知當重新整理物化檢視時,只需要根據SEQUENCE$$列給出的順序,透過M_ROW$$定位到基表的記錄,如果是UPDATE操作,透過CHANGE_VECTOR$$定位到欄位,然後根據基表中的資料重複執行DML操作。
如果物化檢視日誌只針對一個物化檢視,那麼重新整理過程很簡單,按照上面分析的執行就可以,oracle自己會將物化檢視日誌記錄清除掉。但是對於多個基於同一個表的物化檢視的快速重新整理是怎樣的呢?物化檢視在重新整理時還必須判斷哪些物化檢視日誌記錄是當前物化檢視重新整理需要的,哪些是不需要的。而且,物化檢視還必須確定,在重新整理物化檢視後,物化檢視日誌中哪些記錄是需要清除的,
哪些是不需要清除的。這時日誌表中的SNAPTIME$$ 和user_mviews,LAST_REFRESH_DATE就起到作用了,透過這兩個欄位就是來標識什麼情況下要重新整理和清除日誌的。
下面我們透過一個實驗來展示一下多個基於同一個表的物化檢視的重新整理過程。
yang@rac1>create materialized view mv_t_id refresh fast as select id ,count(1) from t group by id;
Materialized view created.
yang@rac1>create materialized view mv_t_name refresh fast as select name ,count(1) from t group by name;
Materialized view created.
yang@rac1>create materialized view mv_t_idna refresh fast as select id,name ,count(1) from t group by id,name;
Materialized view created.
yang@rac1>insert into t values(1,'a',1);
1 row created.
yang@rac1>insert into t values(2,'b',2);
1 row created.
yang@rac1>insert into t values(3,'lily',3);
1 row created.
yang@rac1>insert into t values(4,'yang',4);
1 row created.
yang@rac1>update t set name ='yang' where id=2;
1 row updated.
yang@rac1>delete t where id=4;
1 row deleted.
yang@rac1>select id, name, m_row$$, snaptime$$, dmltype$$ from mlog$_t;
        ID NAME   M_ROW$$                   SNAPTIME$$            D
---------- ------ ------------------------- --------------------- -
         1 a      AAAgOPAAOAAA9AOAAA        Jan 01 4000 00:00:00  I
         2 b      AAAgOPAAOAAA9AOAAB        Jan 01 4000 00:00:00  I
         3 lily   AAAgOPAAOAAA9AOAAC        Jan 01 4000 00:00:00  I
         4 yang   AAAgOPAAOAAA9AOAAD        Jan 01 4000 00:00:00  I
         2 b      AAAgOPAAOAAA9AOAAB        Jan 01 4000 00:00:00  U
         2 yang   AAAgOPAAOAAA9AOAAB        Jan 01 4000 00:00:00  U
         4 yang   AAAgOPAAOAAA9AOAAD        Jan 01 4000 00:00:00  D

7 rows selected.
從檢視user_mview_refresh_times來看所有物化檢視上次被重新整理的時間。
yang@rac1>select name ,last_refresh from user_mview_refresh_times;

NAME                           LAST_REFRESH
------------------------------ -----------------------------
MV_T_NAME                      Apr 12 2011 09:22:27
MV_T_ID                        Apr 12 2011 09:22:02
MV_T_IDNA                      Apr 12 2011 09:23:02

6 rows selected.

yang@rac1>select mview_name,last_refresh_date,staleness from user_mviews;

MVIEW_NAME                     LAST_REFRESH_DATE             STALENESS
------------------------------ ----------------------------- -------------------
MV_T_NAME                      Apr 12 2011 09:22:28          FRESH
MV_T_ID                        Apr 12 2011 09:22:02          FRESH
MV_T_IDNA                      Apr 12 2011 09:23:02          FRESH

yang@rac1>insert into t values(5,'LILY',5);
1 row created.
yang@rac1>COMMIT;
Commit complete.
yang@rac1>select mview_name,last_refresh_date,staleness from user_mviews;
MVIEW_NAME                     LAST_REFRESH_DATE             STALENESS
------------------------------ ----------------------------- -------------------
MV_T_NAME                      Apr 12 2011 09:22:28          NEEDS_COMPILE
MV_T_ID                        Apr 12 2011 09:22:02          NEEDS_COMPILE
MV_T_IDNA                      Apr 12 2011 09:23:02          NEEDS_COMPILE
重新整理所有物化檢視。
yang@rac1>exec dbms_mview.refresh('MV_T_NAME');
PL/SQL procedure successfully completed.
yang@rac1>exec dbms_mview.refresh('MV_T_ID');
PL/SQL procedure successfully completed.
yang@rac1>exec dbms_mview.refresh('MV_T_IDNA');
PL/SQL procedure successfully completed.
yang@rac1>select mview_name,last_refresh_date,staleness from user_mviews;
MVIEW_NAME                     LAST_REFRESH_DATE             STALENESS
------------------------------ ----------------------------- -----------
MV_T_NAME                      Apr 12 2011 09:29:50          FRESH
MV_T_ID                        Apr 12 2011 09:29:55          FRESH
MV_T_IDNA                      Apr 12 2011 09:30:03          FRESH

yang@rac1>select id, name, m_row$$, snaptime$$, dmltype$$ from mlog$_t;
no rows selected

可見重新整理所有基於日誌的物化檢視後,物化檢視日誌被自動清除。當僅僅重新整理某個物化檢視的情況

yang@rac1>insert into t values(6,'LILY',6);
1 row created.
yang@rac1>commit;
Commit complete.
yang@rac1>select id, name, m_row$$, snaptime$$, dmltype$$ from mlog$_t;
        ID NAME   M_ROW$$            SNAPTIME$$             D
---------- ------ ------------------ ---------------------- -
         6 LILY   AAAgOPAAOAAA9AOAAF Jan 01 4000 00:00:00   I
yang@rac1>select mview_name,last_refresh_date,staleness from user_mviews;
MVIEW_NAME                     LAST_REFRESH_DATE             STALENESS
------------------------------ ----------------------------- -------------------
MV_T_NAME                      Apr 12 2011 09:29:50          NEEDS_COMPILE
MV_T_ID                        Apr 12 2011 09:29:55          NEEDS_COMPILE
MV_T_IDNA                      Apr 12 2011 09:30:03          NEEDS_COMPILE
所有基於表T的物化檢視都顯示為NEEDS_COMPILE。
yang@rac1>exec dbms_mview.refresh('MV_T_IDNA');
PL/SQL procedure successfully completed.
yang@rac1>select mview_name,last_refresh_date,staleness from user_mviews;
MVIEW_NAME   LAST_REFRESH_DATE             STALENESS
------------ ----------------------------- -------------------
MV_T_NAME    Apr 12 2011 09:29:50          NEEDS_COMPILE
MV_T_ID      Apr 12 2011 09:29:55          NEEDS_COMPILE
MV_T_IDNA    Apr 12 2011 09:33:45          FRESH
注意 MV_T_IDNA的 最新重新整理時間改變了。檢視物化日誌的snaptime$$,也被更新為重新整理的時間了。
yang@rac1>select id, name, m_row$$, snaptime$$, dmltype$$ from mlog$_t;

ID NAME    M_ROW$$              SNAPTIME$$           D
-- ------ -------------------- -------------------- -
6  LILY    AAAgOPAAOAAA9AOAAF  Apr 12 2011 09:33:45  I
再次重新整理一個物化檢視。檢視物化日誌的snaptime$$,並未更改。
yang@rac1>exec dbms_mview.refresh('MV_T_ID');
PL/SQL procedure successfully completed.

yang@rac1>select id, name, m_row$$, snaptime$$, dmltype$$ from mlog$_t;
ID NAME    M_ROW$$              SNAPTIME$$           D
-- ------ -------------------- -------------------- -
6  LILY    AAAgOPAAOAAA9AOAAF  Apr 12 2011 09:33:45  I
在user_mviews中last_refresh_date小於mlog$_t中的snaptime$$的值的都標記為needs_compile;現在基本可以看出多個物化檢視基於同一個表的情況下,物化檢視重新整理的條件
當每個物化檢視對應的LAST_REFRESH_DATE小於物化日誌的snaptime$$的值時,oracle會認為此物化檢視需要重新整理,當然反之,則認為已經重新整理過了
yang@rac1>select mview_name,last_refresh_date,staleness from user_mviews;
MVIEW_NAME                     LAST_REFRESH_DATE             STALENESS
------------------------------ ----------------------------- -------------------
MV_T_NAME                      Apr 12 2011 09:29:50          NEEDS_COMPILE
MV_T_ID                        Apr 12 2011 09:34:30          FRESH
MV_T_IDNA                      Apr 12 2011 09:33:45          FRESH
物化檢視日誌的管理是由oracle自動管理的。當所有基於某個表的物化檢視被重新整理後,oracle會自動刪除已經執行過重新整理的記錄。
yang@rac1>insert into t values(6,'yang',6);
1 row created.
yang@rac1>commit;
Commit complete.
yang@rac1>exec dbms_mview.refresh('MV_T_NAME');
PL/SQL procedure successfully completed.

yang@rac1>select id, name, m_row$$, snaptime$$, dmltype$$ from mlog$_t;
        ID NAME                           M_ROW$$                   SNAPTIME$$                    D
---------- ------------------------------ ------------------------- ----------------------------- -
         6 yang                           AAAgOPAAOAAA9AOAAD        Apr 12 2011 09:36:17          I

yang@rac1>select mview_name,last_refresh_date,staleness from user_mviews;

MVIEW_NAME                     LAST_REFRESH_DATE             STALENESS
------------------------------ ----------------------------- -------------------
MV_FACT                        Apr 12 2011 09:28:08          FRESH
MV_T_NAME                      Apr 12 2011 09:36:17          FRESH
MV_T_ID                        Apr 12 2011 09:34:30          NEEDS_COMPILE
MV_T_IDNA                      Apr 12 2011 09:33:45          NEEDS_COMPILE
yang@rac1>exec dbms_mview.refresh('MV_T_ID');
PL/SQL procedure successfully completed.
yang@rac1>exec dbms_mview.refresh('MV_T_IDNA');
PL/SQL procedure successfully completed.

yang@rac1>select id, name, m_row$$, snaptime$$, dmltype$$ from mlog$_t;
no rows selected
可以看出日誌中的所有記錄被清除。
yang@rac1>select mview_name,last_refresh_date,staleness from user_mviews;
MVIEW_NAME                     LAST_REFRESH_DATE             STALENESS
------------------------------ ----------------------------- -------------------
MV_FACT                        Apr 12 2011 09:28:08          FRESH
MV_T_NAME                      Apr 12 2011 09:36:17          FRESH
MV_T_ID                        Apr 12 2011 09:53:34          FRESH
MV_T_IDNA                      Apr 12 2011 09:53:37          FRESH
最後,簡單總結一下:
物化檢視在重新整理時,會重新整理所有SNAPTIME$$大於本物化檢視上次重新整理時間的記錄,並將所有是4000-01-01 00:00:00的記錄更新為當前重新整理時間。對於其他大於上次重新整理時間的記錄,只重新整理不更改。這樣,當重新整理執行完以後,資料字典中記錄當前物化檢視的上次重新整理時間為當前時刻,這保證了物化檢視日誌中目前所有的記錄都小於或等於重新整理時間。因此,每個物化檢視只要重新整理大於上次重新整理時間的記錄,且保證每次重新整理後,所有記錄的時間都小於等於上次重新整理時間,那麼無論有多少個物化檢視,就可以互不影響的使用同一個物化檢視日誌進行快速重新整理。當物化檢視重新整理完之後,會清除那些SNAPTIME$$列小於所有物化檢視的上次重新整理時間的記錄。

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

相關文章