物化视图的日志清除是由Oracle自动完成的。Oracle会根据物化视图基表上的注册信息和物化视图的刷新来确定何时删除物化视图日志。
物化视图的注册信息的清除则是在删除物化视图的时候进行,但是对于物化视图建立在远端数据库中的情况,物化视图注册信息的清除可能会复杂一些。
如果物化视图建立在本地,则Oracle拥有所有的数据字典信息,因此,本篇所有的测试都是针对远端物化视图进行的。
首先来看清除物化视图日志的例子。这个操作完全有Oracle进行,即使用户没有物化视图日志的DELETE权限也没有关系。
首先,登陆远端数据库建立测试所用基表和物化视图日志:
SQL> CONN TEST/TEST@TEST2已连接。
SQL> CREATE TABLE T1 (ID NUMBER PRIMARY KEY);
表已创建。
SQL> CREATE MATERIALIZED VIEW LOG ON T1;
实体化视图日志已创建。
下面在本地建立数据库链和物化视图:
SQL> CONN YANGTK/YANGTK已连接。
SQL> CREATE DATABASE LINK TEST2 CONNECT TO TEST IDENTIFIED BY TEST USING 'TEST2';
数据库链接已创建。
SQL> CREATE MATERIALIZED VIEW MV_TEST REFRESH FAST AS SELECT * FROM T1@TEST2;
实体化视图已创建。
下面开始测试:
SQL> INSERT INTO T1@TEST2 VALUES (1);
已创建 1 行。
SQL> COMMIT;
提交完成。
SQL> SELECT COUNT(*) FROM MLOG$_T1@TEST2;
COUNT(*)
----------
1
SQL> EXEC DBMS_MVIEW.REFRESH('MV_TEST')
PL/SQL 过程已成功完成。
SQL> SELECT COUNT(*) FROM MLOG$_T1@TEST2;
COUNT(*)
----------
0
可见,物化视图日志在物化视图刷新后自动清除了。不过由于数据库链的连接用户是TEST拥有对MLOG$_T1的删除权限。下面建立一个新的用户,只给MLOG$_T1的查询权限:
SQL> CONN TEST/TEST@TEST2已连接。
SQL> CREATE USER AAA IDENTIFIED BY AAA;
用户已创建。
SQL> GRANT CONNECT, RESOURCE TO AAA;
授权成功。
SQL> GRANT SELECT ON T1 TO AAA;
授权成功。
SQL> GRANT SELECT ON MLOG$_T1 TO AAA;
授权成功。
由于我本地数据库的GLOBAL_NAMES设置为FALSE,因此可以建立另一个不同用户的数据库链指向TEST2库。
SQL> CONN YANGTK/YANGTK已连接。
SQL> SHOW PARAMETER GLOBAL_NAMES
NAME TYPE VALUE
------------------------------------ ----------- ----------------------
global_names boolean FALSE
SQL> DROP MATERIALIZED VIEW MV_TEST;
实体化视图已删除。
SQL> CREATE DATABASE LINK TEST2_NEW CONNECT TO AAA IDENTIFIED BY AAA USING 'TEST2';
数据库链接已创建。
SQL> SELECT COUNT(*) FROM TEST.T1@TEST2_NEW;
COUNT(*)
----------
1
SQL> CREATE MATERIALIZED VIEW MV_TEST2 REFRESH FAST AS SELECT * FROM TEST.T1@TEST2_NEW;
实体化视图已创建。
SQL> INSERT INTO T1@TEST2 VALUES (2);
已创建 1 行。
SQL> SELECT COUNT(*) FROM MLOG$_T1@TEST2;
COUNT(*)
----------
1
SQL> EXEC DBMS_MVIEW.REFRESH('MV_TEST2')
PL/SQL 过程已成功完成。
SQL> SELECT COUNT(*) FROM MLOG$_T1@TEST2;
COUNT(*)
----------
0
可见,物化视图日志的清除完全是Oracle的内容操作,与用户的权限没有关系。
物化视图日志的清除依赖于物化视图的注册信息,那么物化视图的注册信息的清除呢:
SQL> SELECT OWNER, NAME, MVIEW_SITE, MVIEW_ID
2 FROM USER_REGISTERED_MVIEWS@TEST2;
OWNER NAME MVIEW_SITE MVIEW_ID
---------- -------------------- ------------------------------ ----------
YANGTK MV_TEST2 YTK.US.ORACLE.COM 64
SQL> DROP MATERIALIZED VIEW MV_TEST2;
实体化视图已删除。
SQL> SELECT OWNER, NAME, MVIEW_SITE, MVIEW_ID
2 FROM USER_REGISTERED_MVIEWS@TEST2;
未选定行
通过测试不难发现在删除物化视图时,Oracle自动将删除的消息传递到主站点,主站点同时清除物化视图的注册信息。
就是这么简单吗,是的。不过前提是创建物化视图时使用的数据库链存在且可用。
如果删除这个数据库链:
SQL> CREATE MATERIALIZED VIEW MV_TEST2 REFRESH FAST AS SELECT * FROM TEST.T1@TEST2_NEW;
实体化视图已创建。
SQL> SELECT OWNER, NAME, MVIEW_SITE, MVIEW_ID
2 FROM USER_REGISTERED_MVIEWS@TEST2;
OWNER NAME MVIEW_SITE MVIEW_ID
---------- -------------------- ------------------------------ ----------
YANGTK MV_TEST2 YTK.US.ORACLE.COM 65
SQL> DROP DATABASE LINK TEST2_NEW;
数据库链接已删除。
SQL> DROP MATERIALIZED VIEW MV_TEST2;
实体化视图已删除。
SQL> SELECT OWNER, NAME, MVIEW_SITE, MVIEW_ID
2 FROM USER_REGISTERED_MVIEWS@TEST2;
OWNER NAME MVIEW_SITE MVIEW_ID
---------- -------------------- ------------------------------ ----------
YANGTK MV_TEST2 YTK.US.ORACLE.COM 65
在删除数据库链之后删除物化视图,删除物化视图的动作本身并不会报错,但是主站点的物化视图日志不会被清除。
另外Oracle没有聪明到利用其它的等价数据库链的地步,它只会使用物化视图定义中指定的那个数据库链。
下面继续观察物化视图日志的清除情况:
SQL> INSERT INTO T1@TEST2 VALUES (3);
已创建 1 行。
SQL> COMMIT;
提交完成。
SQL> SELECT COUNT(*) FROM MLOG$_T1@TEST2;
COUNT(*)
----------
1
由于物化视图注册信息的存在,物化视图
下面重建数据库链和刚才删除的物化视图:
SQL> CREATE DATABASE LINK TEST2_NEW CONNECT TO AAA IDENTIFIED BY AAA USING 'TEST2';
数据库链接已创建。
SQL> CREATE MATERIALIZED VIEW MV_TEST2 REFRESH FAST AS SELECT * FROM TEST.T1@TEST2_NEW;
实体化视图已创建。
SQL> SELECT OWNER, NAME, MVIEW_SITE, MVIEW_ID
2 FROM USER_REGISTERED_MVIEWS@TEST2;
OWNER NAME MVIEW_SITE MVIEW_ID
---------- -------------------- ------------------------------ ----------
YANGTK MV_TEST2 YTK.US.ORACLE.COM 66
SQL> SELECT COUNT(*) FROM MLOG$_T1@TEST2;
COUNT(*)
----------
1
由于OWNER、NAME和MVIEW_SITE等信息完全一样,Oracle没有建立新的注册信息,而是更新了注册信息,物化视图的ID由原来的65变成了66。
但是这次物化视图的创建并没有清掉物化视图日志。
而且后续的刷新操作也无法清除掉物化视图日志:
SQL> INSERT INTO T1@TEST2 VALUES (4);
已创建 1 行。
SQL> COMMIT;
提交完成。
SQL> SELECT COUNT(*) FROM MLOG$_T1@TEST2;
COUNT(*)
----------
2
SQL> EXEC DBMS_MVIEW.REFRESH('MV_TEST2')
PL/SQL 过程已成功完成。
SQL> SELECT COUNT(*) FROM MLOG$_T1@TEST2;
COUNT(*)
----------
2
这时候物化视图日志已经无法正常的清除了。虽然通过删除物化视图的办法可以去掉注册信息,但是仍然不能解决物化视图日志无法被清除的问题。
SQL> DROP MATERIALIZED VIEW MV_TEST2;
实体化视图已删除。
SQL> SELECT OWNER, NAME, MVIEW_SITE, MVIEW_ID
2 FROM USER_REGISTERED_MVIEWS@TEST2;
未选定行
SQL> CREATE MATERIALIZED VIEW MV_TEST2 REFRESH FAST AS SELECT * FROM TEST.T1@TEST2_NEW;
实体化视图已创建。
SQL> INSERT INTO T1@TEST2 VALUES (5);
已创建 1 行。
SQL> COMMIT;
提交完成。
SQL> SELECT COUNT(*) FROM MLOG$_T1@TEST2;
COUNT(*)
----------
3
SQL> EXEC DBMS_MVIEW.REFRESH('MV_TEST2')
PL/SQL 过程已成功完成。
SQL> SELECT COUNT(*) FROM MLOG$_T1@TEST2;
COUNT(*)
----------
3
SQL> SELECT OWNER, NAME, MVIEW_SITE, MVIEW_ID
2 FROM USER_REGISTERED_MVIEWS@TEST2;
OWNER NAME MVIEW_SITE MVIEW_ID
---------- -------------------- ------------------------------ ----------
YANGTK MV_TEST2 YTK.US.ORACLE.COM 67
查询USER_BASE_TABLE_MVIEWS视图可以发现,MVIEW_ID为65的信息并没有清除掉。
SQL> SELECT OWNER, MASTER, MVIEW_ID FROM USER_BASE_TABLE_MVIEWS@TEST2;
OWNER MASTER MVIEW_ID
---------- ------------------------------ ----------
TEST T1 65
TEST T1 67
现在问题就和以前碰到的一个物化视图日志没有清除的情况完全一样了。具体情况可以参考:物化视图日志没有清除:http://yangtingkun.itpub.net/post/468/19939
不过区别在于,一个是由于数据库损坏重建造成的,而本例中仅仅是由于在删除物化视图之前删除了数据库链。
下面在远端数据库直接清除MVIEW_ID为65的信息就可以了:
SQL> EXEC DBMS_MVIEW.PURGE_MVIEW_FROM_LOG@TEST2(65)
PL/SQL 过程已成功完成。
SQL> SELECT COUNT(*) FROM MLOG$_T1@TEST2;
COUNT(*)
----------
0
本例中只讨论了最简单的物化视图日志内容的清除情况,详细的物化视图日志相关内容可以参考:
物化视图日志结构:http://yangtingkun.itpub.net/post/468/20498
Oracle如何根据物化视图日志快速刷新物化视图:http://yangtingkun.itpub.net/post/468/20584