SMON的作用还包括维护col_usage$列监控统计信息基表。 最早在9i中引入了col_usage$字典基表,其目的在于监控column在SQL语句作为predicate的情况,col_usage$的出现完善了CBO中柱状图自动收集的机制。
create table col_usage$(  obj#              number,                                 /* object number */  intcol#           number,                        /* internal column number */  equality_preds    number,                           /* equality predicates */  equijoin_preds    number,                           /* equijoin predicates */  nonequijoin_preds number,                        /* nonequijoin predicates */  range_preds       number,                              /* range predicates */  like_preds        number,                         /* (not) like predicates */  null_preds        number,                         /* (not) null predicates */  timestamp         date      /* timestamp of last time this row was changed */)  storage (initial 200K next 100k maxextents unlimited pctincrease 0)/create unique index i_col_usage$ on col_usage$(obj#,intcol#)  storage (maxextents unlimited)/
在10g中我们默认使用'FOR ALL COLUMNS SIZE AUTO'的柱状图收集模式,而在9i中默认是'SIZE 1'即默认不收集柱状图,这导致许多9i中正常运行的应用程序在10g中CBO执行计划异常,详见 ;。'SIZE AUTO'意为由Oracle自动决定是否收集柱状图及柱状图的桶数,Oracle自行判断的依据就来源于col_usage$字典基表,若表上的某一列曾在硬解析(hard parse)过的SQL语句中充当过predicate(通俗的说就是where后的condition)的话,我们认为此列上有收集柱状图的必要,那么col_usage$上就会被加入该列曾充当predicate的记录。当DBMS_STATS.GATHER_TABLE_STATS存储过程以'SIZE AUTO'模式执行时,收集进程会检查col_usage$基表以判断哪些列之前曾充当过predicate,若充当过则说明该列有收集柱状图的价值。 SMON会每15分钟将shared pool中的predicate columns的数据刷新到col_usage$基表中(until periodically about every 15 minutes SMON flush the data into the data dictionary),另外当instance shutdown时SMON会扫描col_usage$并找出已被drop表的相关predicate columns记录,并删除这部分"orphaned"孤儿记录。 我们来具体了解col_usage$的填充过程:
SQL> select * from v$version;BANNER----------------------------------------------------------------Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64biPL/SQL Release 10.2.0.4.0 - ProductionCORE    10.2.0.4.0      ProductionTNS for Linux: Version 10.2.0.4.0 - ProductionNLSRTL Version 10.2.0.4.0 - ProductionSQL> select * from global_name;GLOBAL_NAME--------------------------------------------------------------------------------www.oracledatabase12g.comSQL> create table maclean (t1 int);Table created.SQL> select object_id from dba_objects where object_name='MACLEAN'; OBJECT_ID----------   1323013SQL> select * from maclean where t1=1;no rows selectedSQL> set linesize 200 pagesize 2000;注意col_usage$的数据同*_tab_modifications类似,从查询到数据刷新到col_usage$存在一段时间的延迟,所以我们立即查询col_usage$将得不到任何记录,可以手动执行DBMS_STATS.FLUSH_DATABASE_MONITORING_INFO将缓存中的信息刷新到字典上SQL> select * from col_usage$ where obj#=1323013;no rows selectedSQL> oradebug setmypid;Statement processed.针对FLUSH_DATABASE_MONITORING_INFO填充操作做10046 level 12 traceSQL> oradebug event 10046 trace name context forever,level 12;Statement processed.SQL> exec DBMS_STATS.FLUSH_DATABASE_MONITORING_INFO;PL/SQL procedure successfully completed.SQL> select * from col_usage$ where obj#=1323013;      OBJ#    INTCOL# EQUALITY_PREDS EQUIJOIN_PREDS NONEQUIJOIN_PREDS RANGE_PREDS LIKE_PREDS NULL_PREDS TIMESTAMP---------- ---------- -------------- -------------- ----------------- ----------- ---------- ---------- ---------   1323013          1              1              0                 0           0          0          0 19-AUG-11=============10046 trace content====================lock table sys.col_usage$ in exclusive mode nowait在测试中可以发现10.2.0.4上DBMS_STATS.FLUSH_DATABASE_MONITORING_INFO存储过程会优先使用lock in exclusive mode nowait来锁住col_usage$基表,如果lock失败则会反复尝试1100次,若仍不能锁住col_usage$表则放弃更新col_usage$上的数据,避免造成锁等待和死锁。Cksxm.cMonitor Modification Hash Table Basemodification hash table entrymodification hash table chunkmonitoring column usage elementksxmlock_1lock table sys.col_usage$ in exclusive modelock table sys.col_usage$ in exclusive mode nowaitupdate sys.col_usage$   set equality_preds    = equality_preds +                           decode(bitand(:flag, 1), 0, 0, 1),       equijoin_preds    = equijoin_preds +                           decode(bitand(:flag, 2), 0, 0, 1),       nonequijoin_preds = nonequijoin_preds +                           decode(bitand(:flag, 4), 0, 0, 1),       range_preds       = range_preds + decode(bitand(:flag, 8), 0, 0, 1),       like_preds        = like_preds + decode(bitand(:flag, 16), 0, 0, 1),       null_preds        = null_preds + decode(bitand(:flag, 32), 0, 0, 1),       timestamp         = :time where obj# = :ob jn   and intcol# = :colninsert into sys.col_usage$  (obj#,   intcol#,   equality_preds,   equijoin_preds,   nonequijoin_preds,   range_preds,   like_preds,   null_preds,   timestamp)values  (:objn,   :coln,   decode(bitand(:flag, 1), 0, 0, 1),   decode(bitand(:flag, 2), 0, 0, 1),   decode(bitand(:flag, 4), 0, 0, 1),   decode(bitand(:flag, 8), 0, 0, 1),   decode(bitand(:flag, 16), 0, 0, 1),   decode(bitand(:flag, 32), 0, 0, 1),   :time)
使用dbms_stats的'SIZE AUTO'模式收集表上的统计信息会首先参考col_usage$中的predicate columns记录:
SQL> begin  2  3    dbms_stats.gather_table_stats(ownname    => 'SYS',  4                                  tabname    => 'MACLEAN',  5                                  method_opt => 'FOR ALL COLUMNS SIZE AUTO');  6  end;  7  /PL/SQL procedure successfully completed.============10046 level 12 trace content======================SELECT /*+ ordered use_nl(o c cu h) index(u i_user1) index(o i_obj2)               index(ci_obj#) index(cu i_col_usage$)               index(h i_hh_obj#_intcol#) */ C.NAME COL_NAME, C.TYPE# COL_TYPE, C.CHARSETFORM COL_CSF, C.DEFAULT$ COL_DEF, C.NULL$ COL_NULL, C.PROPERTY COL_PROP, C.COL # COL_UNUM, C.INTCOL# COL_INUM, C.OBJ# COL_OBJ, C.SCALE COL_SCALE, H.BUCKET_CNT H_BCNT, (T.ROWCNT - H.NULL_CNT) / GREATEST(H.DISTCNT, 1) H_PFREQ, C.LENGTH COL_LEN, CU.TIMES TAMP CU_TIME, CU.EQUALITY_PREDS CU_EP, CU.EQUIJOIN_PREDS CU_EJP, CU.RANGE_PREDS CU_RP, CU.LIKE_PREDS CU_LP, CU.NONEQUIJOIN_PREDS CU_NEJP, CU.NULL_PREDS NP  FROM SYS.USE        R$ U,       SYS.OBJ$       O,       SYS.TAB$       T,       SYS.COL$       C,       SYS.COL_USAGE$ CU,       SYS.HIST_HEAD$ H WHERE :B3 = '0'   AND U.NAME = :B2   AND O.OWNER# = U.USER#   AND O.TYPE# = 2   AND O.NAME = :B1   AND O.OBJ# = T.OBJ#   AND O.OBJ# = C.OBJ#   AND C.OBJ# = CU.OBJ#(+)   AND C.INTCOL# = CU.INTCOL#(+)   AND C.OBJ# = H.OBJ#(+)   AND C.INTCOL# = H.INTCOL#(+)UNION ALLSELECT /*+ordered use_nl(c) */ C.KQFCONAM COL_NAME, C.KQFCODTY COL_TYPE, DECODE(C.KQFCODTY, 1, 1, 0) COL_CSF, NULL COL_DEF, 0 COL_NULL, 0 COL_PROP, C.KQFCOCNO COL_UNUM, C.KQFCOC NO COL_INUM, O.KQFTAOBJ COL_OBJ, DECODE(C.KQFCODTY, 2, -127, 0) COL_SCALE, H.BUCKET_CNT H_BCNT, (ST.ROWCNT - NULL_CNT) / GREATEST(H.DISTCNT, 1) H_PFREQ, DECODE(C.KQFCODTY, 2, 22, C.KQFCOSIZ) COL_LEN, CU.TIMESTAMP CU_TIME, CU.EQUALITY_PREDS CU_EP, CU.EQUIJOIN_PREDS CU_EJP, CU.RANGE_PREDS CU_RP, CU.LIKE_PREDS CU_LP, CU.NONEQUIJOIN_PREDS CU _NEJP, CU.NULL_PREDS NP  FROM SYS.X$KQFTA    O,       SYS.TAB_STATS$ ST,       SYS.X$KQFCO    C,       SYS.COL_USAGE$ CU,       SYS.HIST_HEAD$ H WHERE :B3 != '0'   AND :B2 = 'SYS'   AND O.KQFTANAM = :B1   AND O.KQFTAOBJ = ST.OBJ#(+)   AND O.KQFTAOBJ = C.KQFCOTOB   AND C.KQFCOTOB = CU.OBJ#(+)   AND C.KQFCOCNO = CU.INTCOL#(+)   AND C.KQFCOTOB = H.OBJ#(+)   AND C.KQFCOCNO = H.INTCO L#(+)
现象 根据Metalink Note<Database Shutdown Immediate Takes Forever, Can Only Do Shutdown Abort [ID 332177.1]>:
Database Shutdown Immediate Takes Forever, Can Only Do Shutdown Abort [ID 332177.1]Applies to:Oracle Server - Enterprise Edition - Version: 9.2.0.4.0This problem can occur on any platform.SymptomsThe database is not shutting down for a considerable time when you issue the command :shutdown immediateTo shut it down in a reasonable time you have to issue the commandshutdown abortTo collect some diagnostics before issuing the shutdown immediate command set a trace event as follows:Connect as SYS (/ as sysdba)SQL> alter session set events '10046 trace name context forever,level 12';SQL> shutdown immediate;In the resultant trace file (within the udump directory) you see something similar to the following :-PARSING IN CURSOR #ndelete from sys.col_usage$ c where not exists   (select 1 from sys.obj$ o where o.obj# = c.obj# )...followed by loads of.....WAIT #2: nam='db file sequential read' ela= 23424 p1=1 p2=4073 p3=1....WAIT #2: nam='db file scattered read' ela= 1558 p1=1 p2=44161 p3=8etcThen eventuallyWAIT #2: nam='log file sync' ela= 32535 p1=4111 p2=0 p3=0...some other SQL....then back toWAIT #2: nam='db file sequential read' ela= 205 p1=1 p2=107925 p3=1WAIT #2: nam='db file sequential read' ela= 1212 p1=1 p2=107926 p3=1WAIT #2: nam='db file sequential read' ela= 212 p1=1 p2=107927 p3=1WAIT #2: nam='db file scattered read' ela= 1861 p1=1 p2=102625 p3=8etc....To verify which objects are involved here you can use a couple of the P1 & P2 values from above:-a) a sequential readSELECT owner,segment_name,segment_typeFROM dba_extentsWHERE file_id=1AND 107927 BETWEEN block_id AND block_id + blocksb) a scattered readSELECT owner,segment_name,segment_typeFROM dba_extentsWHERE file_id=1AND 102625 BETWEEN block_id AND block_id + blocksThe output confirms that the objects areSYS.I_COL_USAGE$  (INDEX)   and   SYS.COL_USAGE$ (TABLE)Finally, issue select count(*) from sys.col_usage$;CauseIf the number of entries in sys.col_usage$ is large then you are very probably hitting the issue raised inBug: 3540022 9.2.0.4.0 RDBMS Base Bug 3221945Abstract: CLEAN-UP OF ENTRIES IN COL_USAGE$Base Bug 3221945 9.2.0.3 RDBMSAbstract: ORA-1631 ON COL_USAGE$Closed as "Not a Bug"However, when a table is dropped, the column usage statistics are not dropped. They are left as they are.When the database is shutdown (in normal mode), then these "orphaned" column usage entries are deleted. The codewhich does this gets called only during normal shutdown.Unless and until the database is shutdown, the col_usage$ table will continue to grow.SolutionTo implement the workaround, please execute the following steps:1. Periodically (eg once a day) run exec DBMS_STATS.FLUSH_DATABASE_MONITORING_INFO;DBMS_STATS.FLUSH_DATABASE_MONITORING_INFO will clean out redundant col_usage$ entries, and whenyou come to shutdown the database you should not have a huge number of entries left to clean up.
该文档指出了在shutdown instance时SMON会着手清理col_usage$中已被drop表的相关predicate columns的"orphaned"记录,如果在本次实例的生命周期中曾生成大量最后被drop的中间表,那么col_usage$中已经堆积了众多的"orphaned"记录,SMON为了完成cleanup工作需要花费大量时间导致shutdown变慢。这个文档还指出定期执行DBMS_STATS.FLUSH_DATABASE_MONITORING_INFO也可以清理col_usage$中的冗余记录。 我们来观察一下SMON的清理工作:
begin  for i in 1 .. 5000 loop    execute immediate 'create table maclean1' || i ||' tablespace fragment as select 1 t1 from dual';    execute immediate 'select * from maclean1' || i || ' where t1=1';  end loop;  DBMS_STATS.FLUSH_DATABASE_MONITORING_INFO;  for i in 1 .. 5000 loop    execute immediate 'drop table maclean1' || i;  end loop;end;/SQL> purge dba_recyclebin;DBA Recyclebin purged.我们可以通过以下查询了解col_usage$上的orphaned记录总数,这也将是在instance shutdown时SMON所需要清理的数目  select count(*)    from sys.col_usage$ c   where not exists (select /*+ unnest */           1            from sys.obj$ o           where o.obj# = c.obj#);  COUNT(*)----------     10224针对SMON做10046 level 12 traceSQL> oradebug setospid 30225;Oracle pid: 8, Unix process pid: 30225, image: oracle@rh2.oracle.com (SMON)SQL> oradebug event 10046 trace name context forever,level 12;Statement processed.SQL> shutdown immediate;=================10046 trace content==================lock table sys.col_usage$ in exclusive mode nowaitdelete from sys.col_usage$ where obj#= :1 and intcol#= :2delete from sys.col_usage$ c where not exists (select /*+ unnest */         1          from sys.obj$ o         where o.obj# = c.obj#)
如何禁止SMON维护col_usage$字典基表 1.设置隐藏参数_column_tracking_level(column usage tracking),该参数默认为1即启用column使用情况跟踪。设置该参数为0,将禁用column tracking,该参数可以在session和system级别动态修改:
SQL> col name for a25SQL> col DESCRIB for a25SQL> SELECT x.ksppinm NAME, y.ksppstvl VALUE, x.ksppdesc describ  2   FROM SYS.x$ksppi x, SYS.x$ksppcv y  3   WHERE x.inst_id = USERENV ('Instance')  4   AND y.inst_id = USERENV ('Instance')  5   AND x.indx = y.indx  6  AND x.ksppinm LIKE '%_column_tracking_level%';NAME                      VALUE      DESCRIB------------------------- ---------- -------------------------_column_tracking_level    1          column usage trackingSQL> alter session set "_column_tracking_level"=0 ;Session altered.SQL> alter system set "_column_tracking_level"=0 scope=both;System altered.
2.关闭DML monitoring,可以通过设置隐藏参数_dml_monitoring_enabled(enable modification monitoring)为false实现,disable dml monitoring对CBO的影响较大,所以我们一般推荐上一种方式:
SQL> SELECT monitoring, count(*) from DBA_TABLES group by monitoring;MON   COUNT(*)--- ----------NO          79YES       2206SQL> alter system set "_dml_monitoring_enabled"=false;System altered.SQL> SELECT monitoring, count(*) from DBA_TABLES group by monitoring;MON   COUNT(*)--- ----------NO        2285实际上dba_tables的monitoring列来源于内部参数_dml_monitoring_enabledSQL> set long 99999SQL> select text from dba_views where view_name='DBA_TABLES';TEXT--------------------------------------------------------------------------------select u.name, o.name, decode(bitand(t.property,2151678048), 0, ts.name, null),       decode(bitand(t.property, 1024), 0, null, co.name),       decode((bitand(t.property, 512)+bitand(t.flags, 536870912)),              0, null, co.name),       decode(bitand(t.trigflag, 1073741824), 1073741824, 'UNUSABLE', 'VALID'),       decode(bitand(t.property, 32+64), 0, mod(t.pctfree$, 100), 64, 0, null),       decode(bitand(ts.flags, 32), 32, to_number(NULL),          decode(bitand(t.property, 32+64), 0, t.pctused$, 64, 0, null)),       decode(bitand(t.property, 32), 0, t.initrans, null),       decode(bitand(t.property, 32), 0, t.maxtrans, null),       s.iniexts * ts.blocksize,       decode(bitand(ts.flags, 3), 1, to_number(NULL),                                      s.extsize * ts.blocksize),       s.minexts, s.maxexts,       decode(bitand(ts.flags, 3), 1, to_number(NULL),                                      s.extpct),       decode(bitand(ts.flags, 32), 32, to_number(NULL),         decode(bitand(o.flags, 2), 2, 1, decode(s.lists, 0, 1, s.lists))),       decode(bitand(ts.flags, 32), 32, to_number(NULL),         decode(bitand(o.flags, 2), 2, 1, decode(s.groups, 0, 1, s.groups))),       decode(bitand(t.property, 32+64), 0,                decode(bitand(t.flags, 32), 0, 'YES', 'NO'), null),       decode(bitand(t.flags,1), 0, 'Y', 1, 'N', '?'),       t.rowcnt,       decode(bitand(t.property, 64), 0, t.blkcnt, null),       decode(bitand(t.property, 64), 0, t.empcnt, null),       t.avgspc, t.chncnt, t.avgrln, t.avgspc_flb,       decode(bitand(t.property, 64), 0, t.flbcnt, null),       lpad(decode(t.degree, 32767, 'DEFAULT', nvl(t.degree,1)),10),       lpad(decode(t.instances, 32767, 'DEFAULT', nvl(t.instances,1)),10),       lpad(decode(bitand(t.flags, 8), 8, 'Y', 'N'),5),       decode(bitand(t.flags, 6), 0, 'ENABLED', 'DISABLED'),       t.samplesize, t.analyzetime,       decode(bitand(t.property, 32), 32, 'YES', 'NO'),       decode(bitand(t.property, 64), 64, 'IOT',               decode(bitand(t.property, 512), 512, 'IOT_OVERFLOW',               decode(bitand(t.flags, 536870912), 536870912, 'IOT_MAPPING', null))),       decode(bitand(o.flags, 2), 0, 'N', 2, 'Y', 'N'),       decode(bitand(o.flags, 16), 0, 'N', 16, 'Y', 'N'),       decode(bitand(t.property, 8192), 8192, 'YES',              decode(bitand(t.property, 1), 0, 'NO', 'YES')),       decode(bitand(o.flags, 2), 2, 'DEFAULT',             decode(s.cachehint, 0, 'DEFAULT', 1, 'KEEP', 2, 'RECYCLE', NULL)),       decode(bitand(t.flags, 131072), 131072, 'ENABLED', 'DISABLED'),       decode(bitand(t.flags, 512), 0, 'NO', 'YES'),       decode(bitand(t.flags, 256), 0, 'NO', 'YES'),       decode(bitand(o.flags, 2), 0, NULL,          decode(bitand(t.property, 8388608), 8388608,                 'SYS$SESSION', 'SYS$TRANSACTION')),       decode(bitand(t.flags, 1024), 1024, 'ENABLED', 'DISABLED'),       decode(bitand(o.flags, 2), 2, 'NO',           decode(bitand(t.property, 2147483648), 2147483648, 'NO',              decode(ksppcv.ksppstvl, 'TRUE', 'YES', 'NO'))),       decode(bitand(t.property, 1024), 0, null, cu.name),       decode(bitand(t.flags, 8388608), 8388608, 'ENABLED', 'DISABLED'),       decode(bitand(t.property, 32), 32, null,                decode(bitand(s.spare1, 2048), 2048, 'ENABLED', 'DISABLED')),       decode(bitand(o.flags, 128), 128, 'YES', 'NO')from sys.user$ u, sys.ts$ ts, sys.seg$ s, sys.obj$ co, sys.tab$ t, sys.obj$ o,     sys.obj$ cx, sys.user$ cu, x$ksppcv ksppcv, x$ksppi ksppiwhere o.owner# = u.user#  and o.obj# = t.obj#  and bitand(t.property, 1) = 0  and bitand(o.flags, 128) = 0  and t.bobj# = co.obj# (+)  and t.ts# = ts.ts#  and t.file# = s.file# (+)  and t.block# = s.block# (+)  and t.ts# = s.ts# (+)  and t.dataobj# = cx.obj# (+)  and cx.owner# = cu.user# (+)  and ksppi.indx = ksppcv.indx  and ksppi.ksppinm = '_dml_monitoring_enabled'