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/22308399/viewspace-750551/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/22308399/viewspace-750551/