STATSPACK資料清除(一)

yangtingkun發表於2008-07-10

Oracle文件推薦的STATSPACK過期資料產生的方法是直接刪除STATS$SNAPSHOT表中的記錄。

 

 

觀察PERFSTAT使用者下的表:

SQL> SELECT TABLE_NAME FROM USER_TABLES;

TABLE_NAME
------------------------------
STATS$BG_EVENT_SUMMARY
STATS$BUFFER_POOL_STATISTICS
STATS$DATABASE_INSTANCE
STATS$DB_CACHE_ADVICE
STATS$DLM_MISC
STATS$ENQUEUE_STAT
.
.
.
STATS$UNDOSTAT
STATS$WAITSTAT

已選擇41行。

用於進行STATSPACK統計表多達41個,那麼僅僅刪除STATS$SNAPSHOT表,能否達到清除資料的目的。

檢查一個已經部署STATSPACK的產品資料庫:

SQL> SELECT JOB, WHAT FROM USER_JOBS;

       JOB WHAT
---------- ----------------------------------------------------------------------
       141 statspack.snap;
     44190 begin delete stats$snapshot where snap_time <= trunc(sysdate) - 61;
           commit; end;

可以看到,這個資料庫就是透過刪除STATS$SNAPSHOT來清除資料的,那麼PERFSTAT使用者下的表中的記錄是否已經被刪除:

SQL> SELECT MIN(SNAP_ID), MAX(SNAP_ID) FROM STATS$SNAPSHOT;

MIN(SNAP_ID) MAX(SNAP_ID)
------------ ------------
       24312        25799

對於STATS$SNAPSHOT表來說,記錄已經清除到了24312,那麼其他的表是否也是如此呢:

SQL> SET SERVEROUT ON SIZE 1000000
SQL> DECLARE
  2     TYPE T_TAB IS TABLE OF VARCHAR2(30) INDEX BY BINARY_INTEGER;
  3     V_TAB T_TAB;
  4     V_RESULT VARCHAR2(32767);
  5  BEGIN
  6     SELECT TABLE_NAME BULK COLLECT INTO V_TAB
  7     FROM USER_TABLES
  8     INTERSECT
  9     SELECT TABLE_NAME FROM USER_TAB_COLUMNS WHERE COLUMN_NAME = 'SNAP_ID' ;
 10     FOR I IN 1..V_TAB.COUNT LOOP
 11             EXECUTE IMMEDIATE 'SELECT ''' || V_TAB(I)
 12                     || ':MIN_ID('' || MIN(SNAP_ID) || ''), MAX_ID('' || MAX(SNAP_ID) || '')'' FROM ' || V_TAB(I)
 13                     INTO V_RESULT;
 14     DBMS_OUTPUT.PUT_LINE(V_RESULT);
 15     END LOOP;
 16  END;
 17  /
STATS$BG_EVENT_SUMMARY:MIN_ID(24312), MAX_ID(25799)
STATS$BUFFER_POOL_STATISTICS:MIN_ID(24312), MAX_ID(25799)
STATS$DATABASE_INSTANCE:MIN_ID(1), MAX_ID(25202)
STATS$DB_CACHE_ADVICE:MIN_ID(24312), MAX_ID(25799)
STATS$DLM_MISC:MIN_ID(), MAX_ID()
STATS$ENQUEUE_STAT:MIN_ID(24312), MAX_ID(25799)
STATS$FILESTATXS:MIN_ID(24312), MAX_ID(25799)
STATS$INSTANCE_RECOVERY:MIN_ID(24312), MAX_ID(25799)
STATS$LATCH:MIN_ID(24312), MAX_ID(25799)
STATS$LATCH_CHILDREN:MIN_ID(), MAX_ID()
STATS$LATCH_MISSES_SUMMARY:MIN_ID(24312), MAX_ID(25799)
STATS$LATCH_PARENT:MIN_ID(), MAX_ID()
STATS$LIBRARYCACHE:MIN_ID(24312), MAX_ID(25799)
STATS$PARAMETER:MIN_ID(24312), MAX_ID(25799)
STATS$PGASTAT:MIN_ID(24312), MAX_ID(25799)
STATS$PGA_TARGET_ADVICE:MIN_ID(24312), MAX_ID(25799)
STATS$RESOURCE_LIMIT:MIN_ID(24312), MAX_ID(25799)
STATS$ROLLSTAT:MIN_ID(24312), MAX_ID(25799)
STATS$ROWCACHE_SUMMARY:MIN_ID(24312), MAX_ID(25799)
STATS$SEG_STAT:MIN_ID(), MAX_ID()
STATS$SESSION_EVENT:MIN_ID(), MAX_ID()
STATS$SESSTAT:MIN_ID(), MAX_ID()
STATS$SGA:MIN_ID(24312), MAX_ID(25799)
STATS$SGASTAT:MIN_ID(24312), MAX_ID(25799)
STATS$SHARED_POOL_ADVICE:MIN_ID(24312), MAX_ID(25799)
STATS$SNAPSHOT:MIN_ID(24312), MAX_ID(25799)
STATS$SQL_PLAN:MIN_ID(), MAX_ID()
STATS$SQL_PLAN_USAGE:MIN_ID(), MAX_ID()
STATS$SQL_STATISTICS:MIN_ID(24312), MAX_ID(25799)
STATS$SQL_SUMMARY:MIN_ID(24312), MAX_ID(25799)
STATS$SQL_WORKAREA_HISTOGRAM:MIN_ID(24312), MAX_ID(25799)
STATS$SYSSTAT:MIN_ID(24312), MAX_ID(25799)
STATS$SYSTEM_EVENT:MIN_ID(24312), MAX_ID(25799)
STATS$TEMPSTATXS:MIN_ID(24312), MAX_ID(25799)
STATS$UNDOSTAT:MIN_ID(1), MAX_ID(25799)
STATS$WAITSTAT:MIN_ID(24312), MAX_ID(25799)

PL/SQL procedure successfully completed.

可以看到,除了個別記錄為空之外,只有STATS$DATABASE_INSTANCESTATS$UNDOSTAT兩張表的記錄沒有被清除掉,其他表的記錄是和STATS$SNAPSHOT表保持一致的。

而觀察上STATS$DATABASE_INSTANCE表可以發現,裡面記錄的應該是資料庫的啟動記錄,這張表的記錄並非根據SNAP_ID進行統計,因此顯然也無法根據SNAP_ID進行刪除。而STATS$UNDOSTAT是根據SNAP_ID進行的統計,而沒有這裡沒有刪除,這是ORACLE的一個bug,可以參考:http://yangtingkun.itpub.net/post/468/466098

那麼現在就有一個問題了,由於使用者只刪除了STATS$SNAPSHOT表的記錄,那麼Oracle是如何對其他表進行清除的呢。

首先想到的就是觸發器,但是當前使用者下並沒有觸發器:

SQL> SELECT * FROM USER_TRIGGERS;

no rows selected

難道PERFSTAT使用者也像基礎資料字典表一樣採用CLUSTER儲存:

SQL> SELECT * FROM USER_CLUSTERS;

no rows selected

但是當前使用者下也沒有CLUSTER

莫非是使用者刪錯了STATS$SNAPSHOT表的SNAP_ID之後,Oracle下次執行STATSPACK.SNAP的時候會自動刪除掉其他表中的對應記錄,不過檢查STATSPACK包中,未發現DELETE語句:

SQL> SELECT COUNT(*) FROM USER_SOURCE
  2  WHERE UPPER(TEXT) LIKE '%DELETE%';

  COUNT(*)
----------
         0

而且測試發現,在刪除STATS$SNAPSHOT表的同時,其他表對應SNAP_ID的記錄也不見了:

SQL> DELETE STATS$SNAPSHOT WHERE SNAP_ID = 24312;

1 row deleted.

SQL> DECLARE                                                 
  2     TYPE T_TAB IS TABLE OF VARCHAR2(30) INDEX BY BINARY_INTEGER;
  3     V_TAB T_TAB;
  4     V_RESULT VARCHAR2(32767);
  5  BEGIN
  6     SELECT TABLE_NAME BULK COLLECT INTO V_TAB
  7     FROM USER_TABLES
  8     INTERSECT
  9     SELECT TABLE_NAME FROM USER_TAB_COLUMNS WHERE COLUMN_NAME = 'SNAP_ID' ;
 10     FOR I IN 1..V_TAB.COUNT LOOP
 11             EXECUTE IMMEDIATE 'SELECT ''' || V_TAB(I)
 12                     || ':MIN_ID('' || MIN(SNAP_ID) || ''), MAX_ID('' || MAX(SNAP_ID) || '')'' FROM ' || V_TAB(I)
 13                     INTO V_RESULT;
 14     DBMS_OUTPUT.PUT_LINE(V_RESULT);
 15     END LOOP;
 16  END;
 17  /
STATS$BG_EVENT_SUMMARY:MIN_ID(24313), MAX_ID(25800)
STATS$BUFFER_POOL_STATISTICS:MIN_ID(24313), MAX_ID(25800)
STATS$DATABASE_INSTANCE:MIN_ID(1), MAX_ID(25202)
STATS$DB_CACHE_ADVICE:MIN_ID(24313), MAX_ID(25800)
STATS$DLM_MISC:MIN_ID(), MAX_ID()
STATS$ENQUEUE_STAT:MIN_ID(24313), MAX_ID(25800)
STATS$FILESTATXS:MIN_ID(24313), MAX_ID(25800)
STATS$INSTANCE_RECOVERY:MIN_ID(24313), MAX_ID(25800)
STATS$LATCH:MIN_ID(24313), MAX_ID(25800)
STATS$LATCH_CHILDREN:MIN_ID(), MAX_ID()
STATS$LATCH_MISSES_SUMMARY:MIN_ID(24313), MAX_ID(25800)
STATS$LATCH_PARENT:MIN_ID(), MAX_ID()
STATS$LIBRARYCACHE:MIN_ID(24313), MAX_ID(25800)
STATS$PARAMETER:MIN_ID(24313), MAX_ID(25800)
STATS$PGASTAT:MIN_ID(24313), MAX_ID(25800)
STATS$PGA_TARGET_ADVICE:MIN_ID(24313), MAX_ID(25800)
STATS$RESOURCE_LIMIT:MIN_ID(24313), MAX_ID(25800)
STATS$ROLLSTAT:MIN_ID(24313), MAX_ID(25800)
STATS$ROWCACHE_SUMMARY:MIN_ID(24313), MAX_ID(25800)
STATS$SEG_STAT:MIN_ID(), MAX_ID()
STATS$SESSION_EVENT:MIN_ID(), MAX_ID()
STATS$SESSTAT:MIN_ID(), MAX_ID()
STATS$SGA:MIN_ID(24313), MAX_ID(25800)
STATS$SGASTAT:MIN_ID(24313), MAX_ID(25800)
STATS$SHARED_POOL_ADVICE:MIN_ID(24313), MAX_ID(25800)
STATS$SNAPSHOT:MIN_ID(24313), MAX_ID(25800)
STATS$SQL_PLAN:MIN_ID(), MAX_ID()
STATS$SQL_PLAN_USAGE:MIN_ID(), MAX_ID()
STATS$SQL_STATISTICS:MIN_ID(24313), MAX_ID(25800)
STATS$SQL_SUMMARY:MIN_ID(24313), MAX_ID(25800)
STATS$SQL_WORKAREA_HISTOGRAM:MIN_ID(24313), MAX_ID(25800)
STATS$SYSSTAT:MIN_ID(24313), MAX_ID(25800)
STATS$SYSTEM_EVENT:MIN_ID(24313), MAX_ID(25800)
STATS$TEMPSTATXS:MIN_ID(24313), MAX_ID(25800)
STATS$UNDOSTAT:MIN_ID(1), MAX_ID(25800)
STATS$WAITSTAT:MIN_ID(24313), MAX_ID(25800)

PL/SQL procedure successfully completed.

而且,當操作回滾的時候,這些表的記錄又恢復了:

SQL> ROLLBACK;

Rollback complete.

SQL> DECLARE
  2     TYPE T_TAB IS TABLE OF VARCHAR2(30) INDEX BY BINARY_INTEGER;
  3     V_TAB T_TAB;
  4     V_RESULT VARCHAR2(32767);
  5  BEGIN
  6     SELECT TABLE_NAME BULK COLLECT INTO V_TAB
  7     FROM USER_TABLES
  8     INTERSECT
  9     SELECT TABLE_NAME FROM USER_TAB_COLUMNS WHERE COLUMN_NAME = 'SNAP_ID' ;
 10     FOR I IN 1..V_TAB.COUNT LOOP
 11             EXECUTE IMMEDIATE 'SELECT ''' || V_TAB(I)
 12                     || ':MIN_ID('' || MIN(SNAP_ID) || ''), MAX_ID('' || MAX(SNAP_ID) || '')'' FROM ' || V_TAB(I)
 13                     INTO V_RESULT;
 14     DBMS_OUTPUT.PUT_LINE(V_RESULT);
 15     END LOOP;
 16  END;
 17  /
STATS$BG_EVENT_SUMMARY:MIN_ID(24312), MAX_ID(25800)
STATS$BUFFER_POOL_STATISTICS:MIN_ID(24312), MAX_ID(25800)
STATS$DATABASE_INSTANCE:MIN_ID(1), MAX_ID(25202)
STATS$DB_CACHE_ADVICE:MIN_ID(24312), MAX_ID(25800)
STATS$DLM_MISC:MIN_ID(), MAX_ID()
STATS$ENQUEUE_STAT:MIN_ID(24312), MAX_ID(25800)
STATS$FILESTATXS:MIN_ID(24312), MAX_ID(25800)
STATS$INSTANCE_RECOVERY:MIN_ID(24312), MAX_ID(25800)
STATS$LATCH:MIN_ID(24312), MAX_ID(25800)
STATS$LATCH_CHILDREN:MIN_ID(), MAX_ID()
STATS$LATCH_MISSES_SUMMARY:MIN_ID(24312), MAX_ID(25800)
STATS$LATCH_PARENT:MIN_ID(), MAX_ID()
STATS$LIBRARYCACHE:MIN_ID(24312), MAX_ID(25800)
STATS$PARAMETER:MIN_ID(24312), MAX_ID(25800)
STATS$PGASTAT:MIN_ID(24312), MAX_ID(25800)
STATS$PGA_TARGET_ADVICE:MIN_ID(24312), MAX_ID(25800)
STATS$RESOURCE_LIMIT:MIN_ID(24312), MAX_ID(25800)
STATS$ROLLSTAT:MIN_ID(24312), MAX_ID(25800)
STATS$ROWCACHE_SUMMARY:MIN_ID(24312), MAX_ID(25800)
STATS$SEG_STAT:MIN_ID(), MAX_ID()
STATS$SESSION_EVENT:MIN_ID(), MAX_ID()
STATS$SESSTAT:MIN_ID(), MAX_ID()
STATS$SGA:MIN_ID(24312), MAX_ID(25800)
STATS$SGASTAT:MIN_ID(24312), MAX_ID(25800)
STATS$SHARED_POOL_ADVICE:MIN_ID(24312), MAX_ID(25800)
STATS$SNAPSHOT:MIN_ID(24312), MAX_ID(25800)
STATS$SQL_PLAN:MIN_ID(), MAX_ID()
STATS$SQL_PLAN_USAGE:MIN_ID(), MAX_ID()
STATS$SQL_STATISTICS:MIN_ID(24312), MAX_ID(25800)
STATS$SQL_SUMMARY:MIN_ID(24312), MAX_ID(25800)
STATS$SQL_WORKAREA_HISTOGRAM:MIN_ID(24312), MAX_ID(25800)
STATS$SYSSTAT:MIN_ID(24312), MAX_ID(25800)
STATS$SYSTEM_EVENT:MIN_ID(24312), MAX_ID(25800)
STATS$TEMPSTATXS:MIN_ID(24312), MAX_ID(25800)
STATS$UNDOSTAT:MIN_ID(1), MAX_ID(25800)
STATS$WAITSTAT:MIN_ID(24312), MAX_ID(25800)

PL/SQL procedure successfully completed.

這種DML語句的特性和觸發器特性很相似,可是資料庫中明明沒有觸發器,難道是內部觸發器造成的:

SQL> ALTER SESSION SET SQL_TRACE = TRUE;

Session altered.

SQL> DELETE STATS$SNAPSHOT WHERE SNAP_ID = 24312;

1 row deleted.

SQL> ALTER SESSION SET SQL_TRACE = FALSE;

Session altered.

檢查對應的TRACE檔案:

/opt/oracle/admin/data01/udump/data01_ora_3265.trc
Oracle9i Enterprise Edition Release 9.2.0.4.0 - 64bit Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.4.0 - Production
ORACLE_HOME = /opt/oracle/product/9.2.0
System name: SunOS
Node name: bjdb03
Release: 5.8
Version: Generic_108528-22
Machine: sun4u
Instance name: data01
Redo thread mounted by this instance: 1
Oracle process number: 59
Unix process pid: 3265, image: oracle@bjdb03 (TNS V1-V3)

*** 2008-07-07 18:16:47.055
*** SESSION ID:(70.9231) 2008-07-07 18:16:47.023
APPNAME mod='SQL*Plus' mh=3669949024 act='' ah=4029777240
=====================
PARSING IN CURSOR #1 len=34 dep=0 uid=87 ct=42 lid=87 tim=5678516443929 hv=4177740527 ad='e4193760'
ALTER SESSION SET SQL_TRACE = TRUE
END OF STMT
EXEC #1:c=0,e=10102,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,tim=5678516411866
*** 2008-07-07 18:16:58.285
=====================
PARSING IN CURSOR #1 len=43 dep=0 uid=87 ct=7 lid=87 tim=5678527411060 hv=2481130925 ad='ea68a600'
DELETE STATS$SNAPSHOT WHERE SNAP_ID = 24312
END OF STMT
PARSE #1:c=0,e=92291,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,tim=5678527411039
=====================
PARSING IN CURSOR #2 len=103 dep=1 uid=0 ct=7 lid=0 tim=5678527418652 hv=1216362171 ad='fdc5ff10'
 delete from "PERFSTAT"."STATS$PGASTAT" where "SNAP_ID" = :1 and "DBID" = :2 and "INSTANCE_NUMBER" = :3
END OF STMT
PARSE #2:c=0,e=2896,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=0,tim=5678527418641
EXEC #2:c=0,e=1848,p=0,cr=2,cu=39,mis=0,r=13,dep=1,og=4,tim=5678527420771
=====================
PARSING IN CURSOR #3 len=105 dep=1 uid=0 ct=7 lid=0 tim=5678527421650 hv=1380708534 ad='fd561fc8'
 delete from "PERFSTAT"."STATS$PARAMETER" where "SNAP_ID" = :1 and "DBID" = :2 and "INSTANCE_NUMBER" = :3
END OF STMT
PARSE #3:c=0,e=758,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=0,tim=5678527421642
EXEC #3:c=20000,e=36565,p=0,cr=5,cu=1060,mis=0,r=259,dep=1,og=4,tim=5678527458447
=====================
PARSING IN CURSOR #4 len=104 dep=1 uid=0 ct=7 lid=0 tim=5678527459408 hv=3658160377 ad='fd5b0608'
 delete from "PERFSTAT"."STATS$ROLLSTAT" where "SNAP_ID" = :1 and "DBID" = :2 and "INSTANCE_NUMBER" = :3
END OF STMT
PARSE #4:c=0,e=803,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=0,tim=5678527459399
EXEC #4:c=10000,e=1671,p=0,cr=2,cu=41,mis=0,r=13,dep=1,og=4,tim=5678527461347
=====================
.
.
.
=====================
PARSING IN CURSOR #1 len=35 dep=0 uid=87 ct=42 lid=87 tim=5678535939396 hv=855351039 ad='f6c9b728'
ALTER SESSION SET SQL_TRACE = FALSE
END OF STMT
PARSE #1:c=0,e=710,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,tim=5678535939383
EXEC #1:c=0,e=155,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,tim=5678535939717

OracleSTATSPACK的清除做到了Oracle程式碼部分,完全以內部觸發器方式實現,看來STATSPACK的功能也是Oracle資料庫實現的一個部分,而是不在資料庫完成之後追加上去的。

 

 

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

相關文章