zoukankan      html  css  js  c++  java
  • 了解你所不知道的SMON功能(四):维护col_usage$字典基表

    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执行计划异常,详见<dbms_stats收集模式在9i和10g上的区别>;。'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 - 64bi
    PL/SQL Release 10.2.0.4.0 - Production
    CORE    10.2.0.4.0      Production
    TNS for Linux: Version 10.2.0.4.0 - Production
    NLSRTL Version 10.2.0.4.0 - Production
    
    SQL> select * from global_name;
    
    GLOBAL_NAME
    --------------------------------------------------------------------------------
    www.oracledatabase12g.com
    
    SQL> create table maclean (t1 int);
    Table created.
    
    SQL> select object_id from dba_objects where object_name='MACLEAN';
    
     OBJECT_ID
    ----------
       1323013
    
    SQL> select * from maclean where t1=1;
    
    no rows selected
    
    SQL> 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 selected
    
    SQL> oradebug setmypid;
    Statement processed.
    
    针对FLUSH_DATABASE_MONITORING_INFO填充操作做10046 level 12 trace
    
    SQL> 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.c
    Monitor Modification Hash Table Base
    modification hash table entry
    modification hash table chunk
    monitoring column usage element
    ksxmlock_1
    lock table sys.col_usage$ in exclusive mode
    lock table sys.col_usage$ in exclusive mode nowait
    
    update 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# = :coln
    
    insert 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 ALL
    SELECT /*+
    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.0
    This problem can occur on any platform.
    Symptoms
    
    The database is not shutting down for a considerable time when you issue the command :
    shutdown immediate
    
    To shut it down in a reasonable time you have to issue the command
    shutdown abort
    
    To 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 #n
    delete 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=8
    
    etc
    
    Then eventually
    
    WAIT #2: nam='log file sync' ela= 32535 p1=4111 p2=0 p3=0
    
    ...some other SQL....then back to
    
    WAIT #2: nam='db file sequential read' ela= 205 p1=1 p2=107925 p3=1
    WAIT #2: nam='db file sequential read' ela= 1212 p1=1 p2=107926 p3=1
    WAIT #2: nam='db file sequential read' ela= 212 p1=1 p2=107927 p3=1
    WAIT #2: nam='db file scattered read' ela= 1861 p1=1 p2=102625 p3=8
    etc....
    
    To verify which objects are involved here you can use a couple of the P1 & P2 values from above
    :-
    
    a) a sequential read
    SELECT owner,segment_name,segment_type
    FROM dba_extents
    WHERE file_id=1
    AND 107927 BETWEEN block_id AND block_id + blocks
    
    b) a scattered read
    SELECT owner,segment_name,segment_type
    FROM dba_extents
    WHERE file_id=1
    AND 102625 BETWEEN block_id AND block_id + blocks
    
    The output confirms that the objects are
    
    SYS.I_COL_USAGE$  (INDEX)   and   SYS.COL_USAGE$ (TABLE)
    
    Finally, issue select count(*) from sys.col_usage$;
    
    Cause
    
    If the number of entries in sys.col_usage$ is large then you are very probably hitting the issue raised in
    
    Bug: 3540022 9.2.0.4.0 RDBMS Base Bug 3221945
    Abstract: CLEAN-UP OF ENTRIES IN COL_USAGE$
    
    Base Bug 3221945 9.2.0.3 RDBMS
    Abstract: 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 code
    which does this gets called only during normal shutdown.
    
    Unless and until the database is shutdown, the col_usage$ table will continue to grow.
    Solution
    To 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 when
    you 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 trace
    
    SQL> 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 nowait
    
    delete from sys.col_usage$ where obj#= :1 and intcol#= :2
    
    delete 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 a25
    SQL> col DESCRIB for a25
    SQL> 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 tracking
    
    SQL> 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          79
    YES       2206
    
    SQL> 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_enabled
    
    SQL> set long 99999
    
    SQL> 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 ksppi
    where 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'
  • 相关阅读:
    C++ Programming with TDD之一:GMOCK框架简介
    Linux Programming之MySQL
    Python之自动单元测试之一(unittest使用实例)
    关于过去的这一个月——面试经历
    谈谈Python中对象拷贝
    C++之Effective STL学习笔记Item21
    C++之Effective STL学习笔记Item7
    C++之Effective STL学习笔记Item20
    C++之Effective STL学习笔记Item14
    Coding Rules
  • 原文地址:https://www.cnblogs.com/macleanoracle/p/2967803.html
Copyright © 2011-2022 走看看