【内部原理】模拟Cursor: Pin S on X并解释X$mutex_sleep.location

 

X$mutex_sleep.location 的官方解释是 Location that attempted to acquire the latch (Mutex’s code location (where field)  ,这个解释看上去还是有些模糊,我们来做个试验看看

 

我们简单地使用 gdb来模拟一个Curosr : Pin S on X等待事件,并以此事件来解释X$mutex_sleep.location 的真实含义
我们会打开3个session 分别为A、B、C , 一个gdb , 和一个sqlplus 用以oradebug

 

Session A:

 

 

[oracle@mlab2 ~]$ sqlplus  / as sysdba

SQL*Plus: Release 11.2.0.4.0 Production on Tue Oct 8 21:40:19 2013

Copyright (c) 1982, 2013, Oracle.  All rights reserved.

Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
CORE    11.2.0.4.0      Production
TNS for Linux: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production

create table emp as select * from scott.emp;

SQL> var b1 number;
SQL> begin :b1 := 7902; end;
  2  /

PL/SQL procedure successfully completed.

SQL> select * from emp where empno = :b1
  2  /

     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM
---------- ---------- --------- ---------- --------- ---------- ----------
    DEPTNO
----------
      7902 FORD       ANALYST         7566 03-DEC-81       3000
        20

 

 

 

获取session A的SPID , 使用gdb attach到该SPID

 

 

[oracle@mlab2 ~]$ gdb $ORACLE_HOME/bin/oracle 26868

break kxsPeekBinds
bt 4
end 
cont

 

 

 

登录session B 并flush shared pool:

 

 

alter system flush shared_pool;

 

 

登录session C 并执行上面的查询语句:

 

 

var b1 number;
begin :b1 := 7902; end;
/
select * from emp where empno = :b1
/

 

 

 

如上如果顺利的话session C会hang在cursor: pin S wait on X 等待事件上, 而gdb中显示 session A 的stack call为:

 

 

 

(gdb) cont
Continuing.

Breakpoint 1, 0x0000000002123fa0 in kxsPeekBinds ()
#0  0x0000000002123fa0 in kxsPeekBinds ()
#1  0x00000000025b821c in opitca ()
#2  0x0000000001ecf7a9 in kksSetBindType ()
#3  0x0000000009641a89 in kksfbc ()
(gdb) bt
#0  0x0000000002123fa0 in kxsPeekBinds ()
#1  0x00000000025b821c in opitca ()
#2  0x0000000001ecf7a9 in kksSetBindType ()
#3  0x0000000009641a89 in kksfbc ()
#4  0x00000000095bff0a in opiexe ()
#5  0x0000000001ba5992 in kpoal8 ()
#6  0x00000000095bbdad in opiodr ()
#7  0x00000000097a629f in ttcpip ()
#8  0x000000000186470e in opitsk ()
#9  0x0000000001869235 in opiino ()
#10 0x00000000095bbdad in opiodr ()
#11 0x00000000018607ac in opidrv ()
#12 0x0000000001e3a48f in sou2o ()
#13 0x0000000000a29265 in opimai_real ()
#14 0x0000000001e407ad in ssthrdmain ()
#15 0x0000000000a291d1 in main ()

 

 

 

即 session A在kxsPeekBinds 窥视绑定变量过程中 以EXCL排它模式PIN住 child cursor,而 session C需要执行该SQL 需要以SHRD PIN该child cursor 并进入cursor: pin S wait on X 等待

 

 

select * from x$mutex_sleep;

ADDR                   INDX    INST_ID MUTEX_TYPE                       MUTEX_TYPE_ID LOCATION_ID LOCATION                                     SLEEPS  WAIT_TIME
---------------- ---------- ---------- -------------------------------- ------------- ----------- ---------------------------------------- ---------- ----------
00007F6D82F54108         15          1 Cursor Pin                                   7           5 kkslce [KKSCHLPIN2]                         1500022          0

SQL>  select mutex_type,location_id,location,sleeps from x$mutex_sleep_history order by sleeps asc;

MUTEX_TYPE                       LOCATION_ID LOCATION                                     SLEEPS
-------------------------------- ----------- ---------------------------------------- ----------
Cursor Pin                                 5 kkslce [KKSCHLPIN2]                               1
Library Cache                            109 kglllal1 109                                      1
Library Cache                             95 kglpndl1  95                                      1
Library Cache                             57 kgllkc1   57                                      1
Cursor Pin                                 5 kkslce [KKSCHLPIN2]                               1
Cursor Pin                                 5 kkslce [KKSCHLPIN2]                               1
Library Cache                             32 kglini1   32                                      1
Library Cache                              4 kglpin1   4                                       1
Library Cache                              2 kglget2   2                                       1
Library Cache                             62 kglhdgn1  62                                      1
Library Cache                            106 kglhdgn2 106                                      1
Library Cache                              4 kglpin1   4                                       1
Library Cache                              4 kglpin1   4                                       1
Library Cache                            111 kglllal3 111                                      1
Library Cache                              4 kglpin1   4                                       1
Library Cache                             85 kgllkdl1  85                                      1
Library Cache                             62 kglhdgn1  62                                      1
Library Cache                             62 kglhdgn1  62                                      1
Cursor Pin                                 5 kkslce [KKSCHLPIN2]                               1
Library Cache                            111 kglllal3 111                                      1
Library Cache                             85 kgllkdl1  85                                      1
Library Cache                             32 kglini1   32                                      1
Cursor Parent                             34 kkscsAddChildNode [KKSPRTLOC34]                   1
Library Cache                             62 kglhdgn1  62                                      1
Library Cache                             62 kglhdgn1  62                                      1
Cursor Pin                                 5 kkslce [KKSCHLPIN2]                               2
Library Cache                             90 kglpnal1  90                                      2
Cursor Pin                                 5 kkslce [KKSCHLPIN2]                               2
Library Cache                             57 kgllkc1   57                                      2
Library Cache                            106 kglhdgn2 106                                      2
Library Cache                            119 kglUpgradeLock 119                                2
Library Cache                            102 kglhdgc1  102                                     2
Library Cache                            106 kglhdgn2 106                                      2
Library Cache                             57 kgllkc1   57                                      2
Library Cache                             90 kglpnal1  90                                      2
Cursor Pin                                 5 kkslce [KKSCHLPIN2]                               2
Cursor Pin                                 5 kkslce [KKSCHLPIN2]                               3
Library Cache                            112 kgllldl2 112                                      3
Library Cache                            106 kglhdgn2 106                                      3
Cursor Pin                                 5 kkslce [KKSCHLPIN2]                               3
Library Cache                              4 kglpin1   4                                       3
Library Cache                             57 kgllkc1   57                                      3
Cursor Pin                                 5 kkslce [KKSCHLPIN2]                               3
Library Cache                             57 kgllkc1   57                                      3
Library Cache                              4 kglpin1   4                                       3
Cursor Pin                                 5 kkslce [KKSCHLPIN2]                               4
Library Cache                             57 kgllkc1   57                                      4
Cursor Pin                                 5 kkslce [KKSCHLPIN2]                               4
Cursor Pin                                 5 kkslce [KKSCHLPIN2]                               4
Cursor Pin                                 5 kkslce [KKSCHLPIN2]                               5
Cursor Pin                                 5 kkslce [KKSCHLPIN2]                               6
Library Cache                              4 kglpin1   4                                       6
Cursor Pin                                 5 kkslce [KKSCHLPIN2]                             179
Cursor Pin                                 5 kkslce [KKSCHLPIN2]                         2657144

SQL> select event from v$session where sid=138;

EVENT
----------------------------------------------------------------
cursor: pin S wait on X

session C对应的stack call:

SQL> oradebug short_stack;
ksedsts()+465<-ksdxfstk()+32<-ksdxcb()+1927<-sspuser()+112<-__sighandler()<-__select()+19<-skgpwwait()+332<-kgxWait()+774<-kgxSharedExamine()+568
<-kxsGetRuntimeLock()+240<-kkscsCheckCursor()+556<-kkscsSearchChildList()+1171<-kksfbc()+12417<-kkspsc0()+1171<-kksParseCursor()+116<-opiosq0()+2027
<-kpooprx()+274<-kpoal8()+842<-opiodr()+917<-ttcpip()+2183<-opitsk()+1710<-opiino()+969<-opiodr()+917<-opidrv()+570<-sou2o()+103<-opimai_real()+133
<-ssthrdmain()+265<-main()+201<-__libc_start_main()+244

kkscsSearchChildList 搜索child cursor list
kkscsCheckCursor 检查child cursor
kxsGetRuntimeLock kgxSharedExamine 尝试获取child cursor上的SHRD 共享PIN
尝试获取失败后 进入Mutex等待 kgxWait

 

 

查询 x$mutex_sleep 发现 location为 kkslce [KKSCHLPIN2]    ,LOCATION_ID为 5 ,  我们通过 systemstate 266来看一下Mutex的情况:

 

SQL> oradebug setmypid
Statement processed.
SQL> oradebug dump systemstate 266;
Statement processed.
SQL> oradebug tracefile_name 
/s01/diag/rdbms/g11r204/G11R204/trace/G11R204_ora_26999.trc

    SO: 0x11b8900d0, type: 4, owner: 0x11b528118, flag: INIT/-/-/0x00 if: 0x3 c: 0x3
     proc=0x11b528118, name=session, file=ksu.h LINE:12729, pg=0
    (session) sid: 138 ser: 29 trans: (nil), creator: 0x11b528118
              flags: (0x41) USR/- flags_idl: (0x1) BSY/-/-/-/-/-
              flags2: (0x40009) -/-/INC
              DID: , short-term DID:
              txn branch: (nil)
              edition#: 100              oct: 3, prv: 0, sql: 0x11412a3e8, psql: 0x10f565680, user: 0/SYS
    ksuxds FALSE at location: 0
    service name: SYS$USERS
    client details:
      O/S info: user: oracle, term: pts/2, ospid: 26921
      machine: mlab2.oracle.com program: sqlplus@mlab2.oracle.com (TNS V1-V3)
      application name: sqlplus@mlab2.oracle.com (TNS V1-V3), hash value=339804622
    Current Wait Stack:
     0: waiting for 'cursor: pin S wait on X'
        idn=0xc97fc340, value=0x8e00000000, where=0x500000000
        wait_id=12 seq_num=13 snap_id=1
        wait times: snap=2.245331 sec, exc=2.245331 sec, total=2.245331 sec
        wait times: max=infinite, heur=1 min 12 sec
        wait counts: calls=0 os=0
        in_wait=1 iflags=0x5a2
    There is at least one session blocking this session.
      Dumping 1 direct blocker(s):
        inst: 1, sid: 142, ser: 253
      Dumping final blocker:
        inst: 1, sid: 142, ser: 253

mutex的idn=0xc97fc340

      KGX Atomic Operation Log 0x10f10c230
       Mutex 0x10ef26e68(142, 0) idn c97fc340 oper LONG_EXCL(18)
       Cursor Pin uid 142 efd 0 whr 1 slp 0
       opr=3 pso=0x1140fa930 flg=0
       pcs=0x10ef26dd0 nxt=(nil) flg=34 cld=0 hd=0x10f24b0a0 par=0x10ef268d0
       ct=1 hsh=0 unp=(nil) unn=0 hvl=ef276c0 nhv=1 ses=0x11b883d50
       hep=0x10ef26e68 flg=80 ld=1 ob=0x10ef25830 ptr=0x1163cc070 fex=0x1163cb430

      KGX Atomic Operation Log 0x10eee2a10
       Mutex 0x10ef26e68(142, 0) idn c97fc340 oper GET_SHRD(1)
       Cursor Pin uid 138 efd 0 whr 5 slp 2074
       opr=2 pso=0x10eedbd60 flg=0
       pcs=0x10ef26dd0 nxt=(nil) flg=34 cld=0 hd=0x10f24b0a0 par=0x10ef268d0
       ct=1 hsh=0 unp=(nil) unn=0 hvl=ef276c0 nhv=1 ses=0x11b883d50
       hep=0x10ef26e68 flg=80 ld=1 ob=0x10ef25830 ptr=0x1163cc070 fex=0x1163cb430

      SO: 0x1140fa930, type: 78, owner: 0x11b883d50, flag: INIT/-/-/0x00 if: 0x3 c: 0x3
       proc=0x11b51fb58, name=LIBRARY OBJECT LOCK, file=kgl.h LINE:8751, pg=0

      LibraryObjectLock:  Address=0x1140fa930 Handle=0x10f24b0a0 Mode=N CanBeBrokenCount=2 Incarnation=2 ExecutionCount=0
        ClusterLock=0x10f10c230 Context=0x7f5dfce48600 User=0x11b883d50 Session=0x11b883d50 ReferenceCount=1
        Flags=CBK/[0020] SavepointNum=0
      LibraryHandle:  Address=0x10f24b0a0 Hash=0 LockMode=N PinMode=X LoadLockMode=0 Status=VALD
        Name:  Namespace=SQL AREA(00) Type=CURSOR(00)
        Statistics:  InvalidationCount=1 ExecutionCount=0 LoadCount=2 ActiveLocks=2 TotalLockCount=3 TotalPinCount=5
        Counters:  BrokenCount=2 RevocablePointer=2 KeepDependency=0 Version=0 BucketInUse=0 HandleInUse=0 HandleReferenceCount=0
        Concurrency:  DependencyMutex=0x10f24b150(0, 0, 0, 0) Mutex=0x11412a528(0, 35, 0, 0)
        Flags=RON/PIN/PN0/EXP/CHD/[10012111]
        WaitersLists:
          Lock=0x10f24b130[0x10f24b130,0x10f24b130]
          Pin=0x10f24b110[0x10f24b110,0x10f24b110]
          LoadLock=0x10f24b188[0x10f24b188,0x10f24b188]
        LibraryObject:  Address=0x10ef25830 HeapMask=0000-0001-0001-0000 Flags=EXS[0000] Flags2=[0000] PublicFlags=[0000]
          DataBlocks:
            Block:  #='0' name=KGLH0^c97fc340 pins=0 Change=NONE
              Heap=0x10f24afe8 Pointer=0x10ef258d0 Extent=0x10ef257b0 Flags=I/-/P/A/-/-
              FreedLocation=0 Alloc=2.304688 Size=3.976562 LoadTime=4377720080
            Block:  #='6' name=SQLA^c97fc340 pins=0 Change=NONE
              Heap=0x10ef26f10 Pointer=0x1163cc070 Extent=0x1163cb430 Flags=I/-/P/A/-/E
              FreedLocation=0 Alloc=10.718750 Size=11.859375 LoadTime=0
        NamespaceDump:
          Child Cursor:  Heap0=0x10ef258d0 Heap6=0x1163cc070 Heap0 Load Time=10-08-2013 21:43:08 Heap6 Load Time=10-08-2013 21:43:08  

      SO: 0x1140fab30, type: 78, owner: 0x11b883d50, flag: INIT/-/-/0x00 if: 0x3 c: 0x3
       proc=0x11b51fb58, name=LIBRARY OBJECT LOCK, file=kgl.h LINE:8751, pg=0

      LibraryObjectLock:  Address=0x1140fab30 Handle=0x11412a3e8 Mode=N CanBeBrokenCount=1 Incarnation=1 ExecutionCount=0

        User=0x11b883d50 Session=0x11b883d50 ReferenceCount=1 Flags=CNB/[0001] SavepointNum=5254b4ac
      LibraryHandle:  Address=0x11412a3e8 Hash=c97fc340 LockMode=N PinMode=0 LoadLockMode=0 Status=VALD
        ObjectName:  Name=select * from emp where empno = :b1
          FullHashValue=2d346c57ec22f350eb98796dc97fc340 Namespace=SQL AREA(00) Type=CURSOR(00) Identifier=3380593472 OwnerIdn=0
        Statistics:  InvalidationCount=1 ExecutionCount=1 LoadCount=3 ActiveLocks=2 TotalLockCount=3 TotalPinCount=1
        Counters:  BrokenCount=1 RevocablePointer=1 KeepDependency=1 Version=0 BucketInUse=2 HandleInUse=2 HandleReferenceCount=0
        Concurrency:  DependencyMutex=0x11412a498(0, 1, 0, 0) Mutex=0x11412a528(0, 35, 0, 0)
        Flags=RON/PIN/TIM/PN0/DBN/[10012841]
        WaitersLists:
          Lock=0x11412a478[0x11412a478,0x11412a478]
          Pin=0x11412a458[0x11412a458,0x11412a458]
          LoadLock=0x11412a4d0[0x11412a4d0,0x11412a4d0]
        Timestamp:  Current=10-08-2013 21:40:55
        HandleReference:  Address=0x11412a5b8 Handle=(nil) Flags=[00]
        LibraryObject:  Address=0x10ef26830 HeapMask=0000-0001-0001-0000 Flags=EXS[0000] Flags2=[0000] PublicFlags=[0000]
          ChildTable:  size='16'
            Child:  id='0' Table=0x10ef276e0 Reference=0x10ef27140 Handle=0x10f24b0a0
        NamespaceDump:
          Parent Cursor:  sql_id=fr63tdr4rzhu0 parent=0x10ef268d0 maxchild=1 plk=y ppn=n

 

 

结论:

 

如上述trace ,以LONG_EXCL(18)方式持有Mutex 0x10ef26e68的是SID=142的session 其whr 即location id 为1, 而对应的 等待cursor:pin S on X的session的whr 为5 sleep 为2074次 ,这里whr=5 对应了 x$mutex_sleep.location , 所以我们可以明确知道 x$mutex_sleep 也即v$MUTEX_SLEEP视图上的 LOCATION字段指的是发起对该Mutex申请的代码路径code location,而不是还持有该Mutex的代码路径或曰内核函数。

 

Oracle中的Low HWM与 High HWM 高水位

在MSSM的FREELIST下, 高水位High Water Mark代表所有相关块, 高水位以上就是未格式化unformatted 的数据块,INSRT数据时不能直接使用。当FREELIST中不包含可插入数据块时 HWM默认每次上升5个数据块。

对于ASSM管理的BITMAP 数据段而言,Oracle允许在数据段的中部出现unformatted blocks未格式化的数据块,  基于以下的原因:

一、在以前 HWM以下的数据块必然是formatted , 为了维护这一点代价是昂贵的:

  1. 长时间持有HW enqueue  队列锁对并发的抑制
  2. 过于频繁的持有HW enqueue在Oracle研发看来是罪恶的

上涨HWM 而不格式化 这样的话更有效率,因为格式化往往涉及到 IO,是一种较慢的操作

 

二、 在直接路径加载过程中,最后的一个extent中的数据块将被全部format 格式化,而如果下一次还是direct load直接路径加载数据的话,它不会从Freelist上获取数据块,而是使用HWM以上新的数据盘区extent。 如果这个数据段是典型的一直在direct load加载数据的话,则可能在freelist上有很多unused block从来不被使用,而被浪费了。 这可能造成空间的浪费,尤其是在Extent size 很大的时候 或者 数据段几乎从来不传统路径插入数据的时候。保留这些数据块为unformatted则可以让加载数据时利用到这些空间空洞

 

为了实现这一点就需要使用2个High Water Mark 即Low HWM和High HWM;  Low HWM以下都是formatted 数据块 而 High HWM以上都是unformatted数据块。

 

 

  Extent Header:: spare1: 0      spare2: 0      #extents: 18413  #blocks: 147304
                  last map  0xabc23541  #maps: 36     offset: 2716
      Highwater::  0xabc23f6c  ext#: 18412  blk#: 3      ext size: 8
  #blocks in seg. hdr's freelists: 0
  #blocks below: 147299
  mapblk  0xabc23541  offset: 325
                   Unlocked
  --------------------------------------------------------
  Low HighWater Mark :
      Highwater::  0xabc23f6c  ext#: 18412  blk#: 3      ext size: 8
  #blocks in seg. hdr's freelists: 0
  #blocks below: 147299
  mapblk  0xabc23541  offset: 325
  Level 1 BMB for High HWM block: 0xabc23ef9
  Level 1 BMB for Low HWM block: 0xabc23ef9
  --------------------------------------------------------
  Segment Type: 1 nl2: 2      blksz: 8192   fbsz: 0
  L2 Array start offset:  0x00001434
  First Level 3 BMB:  0x00000000
  L2 Hint for inserts:  0xabc0000a
  Last Level 1 BMB:  0xabc23ef9
  Last Level II BMB:  0xabc1d5fa
  Last Level III BMB:  0x00000000
     Map Header:: next  0xabc009a1  #extents: 307  obj#: 99039  flag: 0x10000000
  Inc # 0
  Extent Map

 

在顺序读取过程中,那些unformatted数据块将被忽略。关于一个数据块究竟是format了还是没有的信息是存放在2个高水位之间的bitmap中。 当扫描数据段的块时,扫描算法并不参考LOW HWM以下的bitmap block; High HWM用来作为顺序读取该数据段时 停止扫描的位置。

 

格式化数据块一般发生在搜索空间时,如果数据块被发现时unformatted,则格式化一部分数据块(至少16个)。 格式化数据块未必更新Low HWM。

 

何时更新LOW HWM是一个问题: Low HWM在当前extent和之前的extent中的所有数据块均被格式化的情况下被更新,否则不更新。

 

来举2个场景: HENO=高水位盘区号, HBNO= 高水位数据块号,MyEno = My Extent number; MyBno = My block number. :

 

如果(HEno == MyEno && MyBno == HBno+1), 例如在同一个extent中格式化下一个范围的block

如果(HEno + 1 == MyEno && HBno == HExtentSize &&MyBno == 0),例如在前一个extent中所有的数据块均已format 然后要format下一个extent时

 

对于High HWM而言,如果High HWM以下的数据块不够用,则可能上升High HWM,这个过程需要用到HW Enqueue队列锁。High HWM移动的块数目取决于一级bitmap block控制的块数目,HIGH HWM以下包含了所有的L1 bitmap block。

 

 

全面解析9i以后Oracle Latch闩锁原理

Latch闩锁在Oracle中属于 KSL Kernel Services Latching, 而从顶层视图来说 KSL又属于VOS  Virtual Operating System。

 maclean-latch

 

 

Latches 是一种 低级别(low-level)的 锁机制, 初学IT的同学请注意 低级不代表简单, C语言对比java语言要 低级一些但C并不比java简单。

 

在一些文章著作中也将latch称为spin lock 自旋锁。  latch用来保护 共享内存(SGA)中的数据 以及关键的代码区域。   一般我们说有2种latch:

 

1)Test and Set 简称TAS  :

TAS是计算机科学中的专指, test-and-set instruction  指令 用以在一个 原子操作(atomic 例如非中断操作)中写入到一个内存位置 ,并返回其旧的值。 常见的是 值1被写入到该内存位置。 如果多个进程访问同一内存位置, 若有一个进程先开始了test-and-set操作,则其他进程直到第一个进程结束TAS才可以开始另一个TAS。 

关于TAS指令更多信息 可以参考wiki ,包括TAS的伪代码例子:    http://t.cn/zQgATRr        

askmac.cn

在Oracle中Test-And-Set类型的latch使用原生的Test-And-Set指令。 在绝大多数平台上, 零值zero代表latch是 空闲或者可用的 , 而一个非零值代表 latch 正忙或者被持有。  但是仅在HP PA-RISC上 正相反。  TAS latch只有2种状态 : 空闲 或者 忙。

 

2) Compare-And-Swap 简称 CAS

Compare-And-Swap 也是计算机专有名词, Compare-And-Swap(CAS)是一个用在多线程环境中实现同步的 原子指令( atomic )。 该指令将在一个给定值(given value)和 指定内存位置的内容 之间比对,仅在一致的情况下 修改该内存位置的内容为一个 给定的 新值(不是前面那个值)。  这些行为都包含在一个 单独的原子操作中。 原子性保证了该新的值是基于最新的信息计算获得的; 如果该 内存位置的内容在同时被其他线程修改过,则本次写入失败。 该操作的结果必须说明其到底是否执行了 取代动作。 它要么返回一个 布尔类型的反馈, 要么返回从 指定内存地址读取到的值(而不是要写入的值)。

关于CAS的更多信息可以参考 http://t.cn/hcEqh

Oracle中的 Compare-And-Swap Latch也使用原生态的Compare-And-Swap指令。  和TAS Latch类似, 空值代表latch是free的,而一个非空值代表latch正忙。  但是一个CAS latch 可以有多种状态 : 空闲的、 以共享模式被持有 、 以排他模式被持有。 CAS latch可以在同一时间被 多个进程或线程以共享模式持有, 但还是仅有一个进程能以排他模式持有CAS latch。   典型的情况下, 共享持有CAS latch的进程以只读模式访问相关数据, 而一个排他的持有者 目的显然是要写入/修改 对应CAS latch保护的数据。

举例来说, CAS latch的共享持有者是为了扫描一个链表 linked list , 而相反排他的持有者是为了修改这个列表。 共享持有者的总数上线是0x0fffffff即10进制的 268435455。

 

注意几乎所有平台均支持CAS latch, 仅仅只有HP的PA-RISC平台不支持(惠普真奇葩)。 在PA-RISC上CAS latch实际是采用TAS latch。 所以虽然在HP  PA-RISC上代码仍会尝试以共享模式获得一个latch,但是抱歉最终会以 排他模式获得这个latch。

 

 

 

一般 一个latch会包含以下 信息:

  • Latch type 类型 , latch type定义了 是TAS 还是CAS latch, latch  class和 latch number
  • Latch的 level 级别
  • 持有该latch的代码位置where ,例如 使用kslgetl函数获得某个latch,则持有文职为kslgetl
  • 持有该latch的原因
  • nowait模式下获得该latch的次数  V$LATCH.IMMEDIATE_GETS
  • wait模式下第一个尝试失败的次数 V$LATCH .MISSES
  • nowait模式下尝试失败的次数 V$LATCH.IMMEDIATE_MISSES
  • 获取latch失败造成sleep的总时间 X$KSLLTR.KSLLTWSL, V$LATCH.SLEEPS
  • 首次spin成功获得latch的次数 X$KSLLTR.KSLLTHST0, V$LATCH.SPIN_GETS
  • latch wait list等等

 

子闩 child latch

 

当一个单一的latch要保护过多的资源时 会造成许多争用,  在此种场景中 child latch变得很有用。   为了使用child latch,  需要分割原latch保护的资源为多个分区, 最常见的例子是 放入到多个hash buckets里, 并将不同子集的资源分配给一个child latch。 比起每一个hash bucket都去实现一个单独的latch来说, 编程上 使用child latch要方便的多, 虽然这不是我们用户所需要考虑的。  为一个latch 定义多个child latch,则这个latch称为parent latch父闩。  child latch 可以继承 parent latch的一些属性,  这些属性包括 级别和清理程序。  换句话说, child latch就像是parent 父闩的拷贝一样。

经典情况下, 在SGA 初始化过程中child latch将被分配和初始化(startup nomount)。但在目前版本中(10/11g)中也允许在实例启动后 创建和删除latch。

 

child latch又可以分成2种:

  • 允许一个进程/线程在同一时刻持有2个兄弟child latch
  • 不允许一个进程/线程在同一时刻持有2个兄弟child latch

 

因为child latch从parent latch那里继承了属性,所以注意 child latch的 latch level和 parent 父闩是一样的。 因为 一个进程/线程 不能在同一时间  持有2个latch level一样的闩,所以正常情况下 一个进程/线程 也不能同一时间 持有2个兄弟child latch。

 

回到我们说的hash bucket的例子里来, 假设一个进程/线程有 将一个resource从一个hash bucket 移动到另一个hash bucket的需求,在此场景中就需要 同时持有2个兄弟child latch。  但是如果允许这种同时持有2个兄弟child latch的行为发生的话, 那么很容易造成死锁deadlock的麻烦。 oracle 不允许 进程/线程任意地同时获得2个兄弟child latch,由于此种操作很容易引起死锁。  由此引入了一些规则 :  兄弟child latch必须是相关的child number ,且进程/线程只能以特性的顺序来同时get 2个兄弟child latch,即child number 从大到小低贱的顺序。

 

此外需要注意的是仅有TAS latch可以同时get多个兄弟child latch,目前还不支持 CAS的latch。

 

Latch 清理恢复

 

Oracle中定义了一个latch, 就需要这个latch对应的清理函数cleanup function,这个函数在以下2个场景中生效:

  1. 当某个latch被持有,但是持有进程遇到了某一个错误                                      ==》主动
  2. 当持有latch的进程die掉,需要PMON进程前去恢复这个latch的状态        ==》被动

 

经典情况下, 执行清理函数的进程要么把正在执行过程中的操作回滚掉 ,要么前滚掉。 为了为 前滚(rolling forward)或者回滚(rolling back)提供必要的信息, oracle在latch结构中加入了recovery的结构,其中包含了这个正在执行过程中的操作的日志信息, 这些日志信息必须包含足以前滚或者回滚的数据。  如我们以前讲过的, 理论上oracle进程可能在运行任何指令的时候意外终止,所以清理恢复是常事。

 

清理恢复最恶心的bug是PMON 执行cleanup function时因为 代码bug ,把PMON自己给弄dead了, 由于PMON的关键的后台进程,所以这会引起实例终止。

 

 

 

Latch和 10.2.0.2后引入的KGX Mutex对比

 

和 latch一样, kgx mutex也是用来控制串行化访问SGA中数据的 ,但仍有一些重要区别:

  1. KGX mutex要比  CAS latch更轻量级, mutex 结构大约为16个字节, 而一个latch结构大约是100个字节。 因此 mutex嵌入到大量其他对象结构中是可行的, 因为他的struct 足够小
  2. 之所以mutex可以提供 更小的结构 很廉价的成本,其原因是 使用mutex有一个简单的前提假设: 对于mutex的争用是很小的。 因此没有为mutex那样提供一个优化过的wait  list , mutex做更多的 SPIN & WAIT 并消耗更多的CPU。   此外mutex也没有提供任何死锁检测和预防机制,这些都完全取决于Kgx mutex的用户自身的行为。
  3. Latch在 内部视图(例如X$KSLLT)中提供 全面的诊断信息。 KGX mutex在(x$mutex_sleep、x$mutex_sleep_history等内部视图)中提供部分信息, 同时也允许其用户在回调程序中用特定信息填充这些视图。
  4. 除了共享和排他模式之外, KGX mutex还提供一种examine 模式, 允许其在不以共享或排他模式持有mutex的情况下client检查一个mutex的状态以及其用户数据。 这种模式是latch所没有的

 

Latch 和Enqueue lock队列锁对比,以下是latch和enqueue的几个重大区别:

 

  1. 在典型情况下,latch被认为将仅仅被持有很短的一段时间(ms级别),而enqueue 将被持有 比之长得多的多的时间(秒=》分钟=》小时)。 例如TX 队列事务锁在整个事务的生命周期中被持有 。 latch被设计出来就是为了在 函数运行到某几十个乃至上百个指令过程中被持有,这是很短暂的过程
  2. latch是为了避免同一时间 有一个以上的进程运行相似的代码片段, 而enqueue是为了避免同一时间多于一个的进程访问相同的资源
  3. latch的使用较为简单, 而enqueue的使用则由于命名空间namespace和死锁检测 的问题而较为麻烦
  4. latch只有2个模式 共享和排他,  而enqueue 则支持6个模式
  5. RAC中 latch 总是本地存放在当前实例的SGA中, 而enqueue可以是Local的 也可能是Global的
  6.  9i以前latch不是FIFO的,是抢占式的; 从9i开始 大多数latch也是FIFO了; enqueue始终是FIFO的

 

有同学仍不理解 latch和enqueue的区别的话, 可以这样想一下, latch 保护的SGA中的数据 对用户来说几乎都是不可见的, 例如 cache buffer的hash bucket 对不研究内部原理的用户来说 等于不存在这个玩样,这些东西都是比较简单的数据结构struct ,如果你是开发oracle的人 你会用几百个字节的enqueue 来保护 几个字节的一个变量吗?

而队列锁 TX是针对事务的 , TM是针对 表的,US是针对 undo segment的,这些东西在实例里已经属于比较高级的对象了,也是用户常可见的对象, 维护这些对象 需要考虑 死锁检测、 并发多模式访问、RAC全局锁 等等问题,所以需要用更复杂的enqueue lock。

 

 死锁dead lock

 

为了使得latch使用足够轻量级 ,死锁预防机制十分简单。  由此Oracle开发人员 在构建一个latch时会定义 一个数字级别 level (从 0 到 16 ), 并且Oracle要求它们必须以level增序顺序获取。  若一个进程/线程在持有一个 latch的情况下,要求一个相同或者更低level的latch的话,KSL层会生成一个内部错误, 这种问题称为 “latch hierarchy violation”。

 

 

SQL> select distinct level# from v$latch order by 1;

    LEVEL#
----------
         0
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        14
        15
        16

 

仅有以nowait模式get latch时可以以级别(level) 非兼容的级别获得一个latch,但是这种情况非常少。

 

Latch Level 级别

 

Oracle在定义latch level的时候 取决于以下2个原则:

  • 那些latch是在被持有的情况下, 进程/线程还会去get其他的latch?
  • 当已经有latch被进程/线程持有的情况下, 那些latch还会被 get?

如上文dead lock的描述, latch level的一大作用是 帮助减少latch dead lock。

 

Latch Class

latch的类class定义了如下的内容:

  • spin count
  • yield count (number of times we yield cpu before sleeping)
  • wait time sample rate (0 implies it is not enabled)
  • sleep (in microseconds and repeated [see below])

 

对于post/wait 类而言 SLEEP_BUCKET和SLEEP_TIME 是被忽略的。

以下是几个latch class:

Class 0  Post/Wait Class ,绝大多数latch都是该类型

Class 1  Waiter List Latch。 该Latch保护对应latch的Waiter List,这种latch被假定总是只被持有非常短的时间(指令级别),  有充分的理由花费更多的spin count 消耗更多的CPU , 并尽可能减少sleep时间

Class 2   那些由于多种原因,不能使用post/wait机制的latch 。 例如process allocation latch  这个闩 是在一个新进程创建时所需要获取的,但是新进程还没加载post/wait的上下文,显然无法用post/wait , 所以这种 latch不能用post /wait机制

Class  3 非常短持有的latch, 特性与class 1类似。

CLASS_KSLLT字段代表了latch的类型

 

SQL> select CLASS_KSLLT,count(*) from x$kslltr group by CLASS_KSLLT;

CLASS_KSLLT   COUNT(*)
----------- ----------
          2          1
          0        702

SQL> select KSLLTNAM,CLASS_KSLLT from x$kslltr where CLASS_KSLLT=2;

KSLLTNAM                                                         CLASS_KSLLT
---------------------------------------------------------------- -----------
process allocation                                                         2

 

 

从9.0.2 开始 每个latch class的SPIN COUNT、YIELD COUNT 、WAITTIME_SAMPLING 、 SLEEP_TIME[1] ….   SLEEP_TIME[i] 均在参数_latch_class_X中定义 。

 

 

SQL> col name for a20
SQL> col avalue for a20
SQL> col sdesc for a20

SELECT x.ksppinm NAME,y.ksppstvl avalue,x.KSPPDESC sdesc
 FROM SYS.x$ksppi x, SYS.x$ksppcv y
 WHERE x.inst_id = USERENV ('Instance')
 AND y.inst_id = USERENV ('Instance')
 AND x.indx = y.indx
AND x.ksppinm like '%latch%class%';

NAME                 AVALUE               SDESC
-------------------- -------------------- --------------------
_latch_class_0                            latch class 0
_latch_class_1                            latch class 1
_latch_class_2                            latch class 2
_latch_class_3                            latch class 3
_latch_class_4                            latch class 4
_latch_class_5                            latch class 5
_latch_class_6                            latch class 6
_latch_class_7                            latch class 7
_latch_classes                            latch classes override

SQL> select INDX,SPIN,YIELD,WAITTIME,SLEEP0 from X$KSLLCLASS;

      INDX       SPIN      YIELD   WAITTIME     SLEEP0
---------- ---------- ---------- ---------- ----------
         0      20000          0          1       8000
         1      20000          0          1       1000
         2      20000          0          1       8000
         3      20000          0          1       1000
         4      20000          0          1       8000
         5      20000          0          1       8000
         6      20000          0          1       8000
         7      20000          0          1       8000

8 rows selected.

 

 

举例来说  _latch_class_1=”5000 2 0 1000 2000 4000 8000″    则

  • SPIN_COUNT=5000
  • YIELD_COUNT=2
  • wait time sampling: 0 (不收集,一般都是1即收集)
  • 增序的sleep time   1000  => 2000 => 4000 => 8000, 单位是microseconds,超过4次则保持在8000

 

每一个wait class 适应自己对应_latch_class_X中的SPIN_COUNT、YIELD_COUNT等参数 。 而实例参数_SPIN_COUNT只做为向后兼容,若对应的latch Class没有自己的SPIN_COUNT属性才会生效。

由此实际生效的SPIN_COUNT由由以下几个参数 按优先级从高到低生效:

  • 首先是 设置过的_latch_class_X  中的SPIN_COUNT
  • 设置够的_SPIN_COUNT
  • 内部函数

 

注意 除非是oracle support建议你去修改这些latch参数,否则在任何系统中都不该去尝试修改它们,如果你确实遇到了latch free的问题,那么你应当首先做 SQL 调优 和并发调整。

 

 

SPIN 

inception-top

 

 

还记得 电影《inception》盗梦空间里中旋转的陀螺吗, 旋转的陀螺 在英文里就是spinning top。 spin 自旋是 latch话题中一个频率很高的词,但是一直以来我们对自旋的理解都不够彻底 ,下面我们彻底解释 9i以后的自旋SPIN 和 Busy Latch原理。

SPIN 是指 当进程首先尝试获取latch失败后( 一般是别人持有了该latch), 有2种选择 要么是退让CPU(yield CPU) 休眠一段时间后再重新尝试获取latch , 要么是 本进程抱着希望在CPU 上空转,因为如果我不用CPU了 让给别人用了 就会造成context switch上下文切换 (vmstat 里看到的CS),而我在CPU上空转的话就可以等等看这个latch是否会在这段时间里被人家释放, 我的一次空转称为SPIN 一次, 而SPIN_COUNT定义了我在这次总的SPIN 操作里总共SPIN 空转多少次,例如SPIN_COUNT=2000(注意 见上文中对SPIN_COUNT的描述)就是说 我有机会空转 2000次, 空转一次后 我跑去查看一下latch是否被别人释放了,如果没有我继续下一次空转, 如果是释放了 那么我就获得这个latch了,也就是SPIN_GETS成功了。如果SPIN 2000次了还是没有等到释放latch,则SPIN_GETS没有成功, 之后该SLEEP就SLEEP( 9i前后 从9i开始有区别,具体见下文)。

 

如果上述SPIN GET的成功获得了latch,那么因为我没有退让CPU 也就没有上下文切换, 所以显然我获得latch的速度要比直接sleep并重试来的快。

 

另假设我提高了某个latch对应的 spin_count ,例如修改latch_class_1中的SPIN_COUNT为更高的值,则在上述情况下可能SPIN循环的次数更多,也就意味着有更高的概率在 SPIN阶段获得 latch, 而代价是SPIN消耗更高的 CPU时间片。 相反 若降低SPIN_COUNT,则意味着SPIN阶段获得latch的概率降低, SPIN消耗相对少的CPU。

 

在中古的硬件中 可能有仅有1个CPU的系统,虽然现在很少见了, 但是显然在仅有一个CPU的情况下SPIN是无意义的,因为如果你把唯一的一个CPU用来SPIN了,显然 真正持有对应latch的那个进程获取不到CPU,获取不到CPU的结果是它无法释放这个latch。在这种环境里代码自动把spin_count调整为1。

 

 

SPIN 与Latch Busy

 

9i之前的 spin与latch busy 运作伪代码可以点击这里(main for 8i)。

 

从9.0.2开始oracle 开始大量启用 post/wait和latch class机制 , 我们来描述一下 伪代码

 

SLEEPs                  //睡眠次数计数
yields                  //yield 计数  copyright askmac.cn
on_wait_list = FALSE;

while (如果未获得latch)
{
  在对应的latch上SPIN ,循环次数为SPIN_COUNT,SPIN_COUNT 来源一般为 _latch_class_X
    if (获得latch)
	   break; 

  if(yields < YIELD_COUNT)                     // YIELD_COUNT来源为latch_class_X 
  {
    yields++;
	yield CPU ;                               
  }

   else

   {
     yield =0;

	 if (如果latch是post/wait机制的)
	  {
	    on_wait_list=TRUE ; 
		get wait list latch ;                  //获得wait list latch
		add current process to wait list;      //将被进程加入到wait list的尾部
        free wait list latch;                  //释放wait list latch
        wait to be posted;                     //等待被post
	  }
	 else 
	  {
	   wait for SLEEP_TIMES[sleeps] microseconds;   //等待SLEEP_TIME[sleeps]对应的时间 来源为latch_class_X 
	   if ( sleeps	 < SLEEP_BUCKETS)               //SLEEP_BUCKETS 一般为4
	   sleeps++;
	 }
 }
 }

//如果某一刻获得了一个post/wait latch,且本进程在wait list上,则需要从wait list 上把自己移走:

if( 如果我在wait list上)
{
get wait list latch ;                       //获得wait list latch
remove current process from wait list;      //从wait list 上把自己移走
free wait list latch;                       //释放wait list latch
}

 

 

从9i开始绝大多数 latch都是 post/wait class的, 出去少量非post/wait class的latch和PMON进程外, 进程都会进入非时控的sleep (sleep[1]..[sleep[i] i到4以后不再增加) 不会自己醒来, 仅在该latch的持有者 释放该latch 且  等待的进程在wait list的头部的情况下被post唤醒 (awake)。 Oracle 选择这种非时控的sleep的原因 为了避免在miss后引起反复的 上下文交换context switch 以便改善性能。

但是这种实现也存在一种风险, 即需要应对那些 持有latch进程意外终止 和 存在丢失 post的bug的情况。

Script:诊断解析等待和高version count

Script:诊断解析等待和高version count

 

 

select * from 
   (select sql_id, count(child_number) 
      from v$sql_shared_cursor 
     group by sql_id
     order by count(child_number) desc)
where rownum <=5;

-- Script Code
set serveroutput on 

DECLARE
  v_count number;
  v_sql varchar2(500);
  v_sql_id varchar2(30) := '&sql_id';
BEGIN
  v_sql_id := lower(v_sql_id);
  dbms_output.put_line(chr(13)||chr(10));
  dbms_output.put_line('sql_id: '||v_sql_id);
  dbms_output.put_line('------------------------');
  FOR c1 in 
    (select column_name  
       from dba_tab_columns 
      where table_name ='V_$SQL_SHARED_CURSOR' 
        and column_name not in ('SQL_ID', 'ADDRESS', 'CHILD_ADDRESS', 'CHILD_NUMBER', 'REASON')
      order by column_id)
  LOOP
    v_sql := 'select count(*) from V_$SQL_SHARED_CURSOR 
              where sql_id='||''''||v_sql_id||''''||' 
              and '||c1.column_name||'='||''''||'Y'||'''';
    execute immediate v_sql into v_count;
    IF v_count > 0 
    THEN 
      dbms_output.put_line(' - '||rpad(c1.column_name,30)||' count: '||v_count);
    END IF;
  END LOOP;
END;
/

oradebug setmypid
oradebug unlimit;
alter session set events 'immediate trace name systemstate level 266';
 alter session set events 'immediate trace name library_cache level 11';
 alter session set events 'immediate trace name cursordump level 16'; 
oradebug tracefile_name;

SELECT b.*
FROM v$sqlarea a ,
  TABLE(version_rpt(a.sql_id)) b
WHERE loaded_versions >=100;


SELECT b.*
FROM v$sqlarea a ,
  TABLE(version_rpt(NULL,a.hash_value)) b
WHERE loaded_versions>=100;

 

 

version_rpt3_22.sql

用三种方法证明DROP USER(有Table)会触发Object级Checkpoint

预备知识:

 

drop user 能引发checkpoint的前提是 USER下面有TABLE,因为DROP USER 等若隐含了不可flashback drop的DROP TABLE。
如果user下无任何表,则此讨论不成立

 

多种的checkpoint中, drop table 触发的是OBJECT CHECKPOINT

 

其行为为 写出所有节点上属于某个 对象 object 的所有脏dirty buffer到磁盘

可能由以下操作触发:
drop table xx;
drop table xx purge;
truncate table xx;

相关的统计信息有

 

DBWR object drop buffers written
DBWR checkpoints

[Read more…]

【Maclean Liu技术分 享】深入理解Oracle中 Mutex的内部原理

【Maclean Liu技术分 享】深入理解Oracle中 Mutex的内部原理

本文适合对Oracle Mutex/latch有兴趣了解其深入内部原理的同学

下载地址:

【Maclean Liu技术分享】深入理解Oracle中Mutex的内部原理

 

深入理解Oracle中的Mutex

 了解 Oracle Mutex

 

虽然Mutex中文翻译为互斥锁,但为了和OS mutex充分的区别,所以我们在本文里称Oracle Mutex为Mutex。

 

Oracle中的mutex,类似于Latch,是一种低级的串行机制,用以控制对SGA中部分共享数据结构的访问控制。  Oracle中的串行机制有不少,引入它们的目的是避免一个对象出现下述现象:

  • 当某些进程在访问该对象时,该资源被重新分配
  • 当某些进程在修改它时,被其他进程读取
  • 当某些进程在修改它时,被其他进程修改
  • 当某些进程在读取它时,被其他进程修改

 

不同于Latch,Mutex的使用更灵活,用途更多,例如:

  • 哪些需要被mutex保护的共享数据结构可以有自己独立的mutex,即一个对象拥有自己独立的mutex,不像Latch往往一个需要保护大量对象,举例来说,每一个父游标有其对应的mutex, 而每一个子游标也有其对应的mutex
  • 每一个数据结构可能有一个或多个mutex保护,每一个mutex负责保护其结构的不同部分
  • 当然一个mutex也可以用来保护多于一个的数据结构

 

理论上mutex即可以存放在其保护的结构本身中(其实是嵌入在结构里),也可以存放在其他地方。 一般情况下Mutex是在数据结构需要被保护时动态创建出来的。 如是嵌在需要保护结构体内的mutex,则当 所依附的数据结构被清理时 该mutex也将被摧毁。

 

 

Mutex带来的好处

虽然mutex和latch都是Oracle中的串行机制,但是mutex具有一些latch没有的好处

 

更轻量级且更快

Mutex作为Latch的替代品,具有更快速获得,更小等优势。 获取一个mutex进需要大约30~35个指令, 而Latch则需要150~200个指令。一个mutex结构的大小大约为16 bytes,而在10.2版本中一个latch需要112个bytes,在更早的版本中是200个bytes。 从200个bytes 精简到112个是通过减少不必要的统计指标 SLEEP1~SLEEP11、WAITERS_WOKEN, WAITS_HOLDING_LATCH等从而实现的。今后我们将看到更多关于Latch的代码优化。

 

减少伪争用

典型情况下一个Latch保护多个对象。 当一个Latch保护多个热对象时,并行地对这些对象的频繁访问让latch本身变成性能的串行点。 这也就是我们此处说的伪争用点, 因为争用是发生在这个串行保护的机制上,而不是进程去访问的对象本身。与latch不同, 使用mutex的情况下Oracle开发人员可以为每一个要保护的数据结构创建一个独立的mutex。 这意味着Latch的那种伪争用将大大减少,因为每一个对象均被自己独立拥有的mutex保护

 

 

 Mutex在一些地方替代了latch和PIN

 

一个Mutex可供多个Oracle进程并行地参考,反过来说进程们可以以S(Shared 共享) mode模式参考一个Mutex。以S mode一起共享参考这个mutex的进程的总数成为参考总数reference count。Mutex自身结构中存放了这个ref count的数据。另一方面,mutex也可以被以X (Exclusive)mode排他模式被仅有一个进程所持有Held。

 

Mutex有2种用途,一方面他们可以充当维护必要串行机制的结构,如同latch那样; 同时也可以充当pin,避免对象被age out。

 

举例来说,mutex结构中包含的ref count信息可以用作替代library cache pin。 在mutex充当cursor pin之前,当一个进程要执行=>pin一个cursor时需要做的是针对性地创建library cache pin和删除这个library cache pin(均为S mode)。mutex充当cursor pin之后,进程只需要增加和减少mutex上的ref count即可。

当某一个进程首次解析一个游标 Cursor,他将临时创建并移除一个library cache pin,但是该进程后续的解析或执行进要求增加或者减少ref count。注意在这个增加/减少ref count的过程中无需acquire latch,这是因为mutex自身能起到限制串行访问修改ref count的作用。 当一个进程要移除自己的mutex pin时,它减少ref count,同样的无需acquire 任何latch。

 

Mutex和Latch的交互

Latches和Mutex 是独立的串行机制, 举例来说一个进程可以同时持有latch和mutex。 在进程异常dead的情况下,一般latch要比Mutex更早被PMON清理。 一般情况下不存在mutex的死锁。 不像latch,在早期版本例如9i之前我们经常遇到latch死锁的问题。

 

Mutex的用途

在版本10.2中仅仅有 KKS 这个内核层是mutex的客户,KKS 意为 Kernel Kompile Shared, 它是Library Cache中的shared cursor游标共享部分层次的代码。 在之后的版本中,ORACLE开发部门更多地使用了Mutex,不局限于KKS。

 

KKS游标共享如何使用Mutex

kks 使用mutex以便保护对于下述基于parent cursor父游标和子游标child cursor的一系列操作。

 

对于父游标parent cursor的操作:

  • 基于发生的不同操作,对应不同的等待事件:
    • 在某个父游标名下创建一个新的游标                     ==> cursor:mutex X
    • 检查一个父游标                                                            ==> cursor:mutex S
    • 绑定值捕获                                                                    ==> cursor:mutex X
  • 保护父游标的mutex嵌入在父游标结构内
  •  针对父游标parent cursor的Mutex类型为’Cursor Parent’ (kgx_kks2).
  •   针对父游标parent cursor的Mutex等待事件均为’ Cursor: mutex *’的形式

 

针对游标统计信息的操作

  • 基于对不同的游标统计信息的操作有不同的等待事件:
    • 构造,更新游标相关的统计信息                                              ==> cursor:mutex X
    • 检测游标相关的统计信息,例如访问V$SQLSTATS            ==> cursor : mutex S
  • 相关的游标可能在父游标中,也可能在游标统计信息相关的hash table上
  • 针对游标统计信息的Mutex类型为Cursor Stat (kgx_kks1)
  •  针对游标统计信息的Mutex等待事件均为’ Cursor: mutex *’的形式

 

 Mutex是如何替代library cache pin来保护cursor heap的?

  • 传统的’library cache pin’在10.2.0.2之后默认被取代, 此处PIN被Mutex及其ref count取代。 当进程执行游标语句时或者需要PIN,或者需要hard parse一个子游标heap。
  • 在版本10.2.0.1中, 使用mutex部分代码替代PIN的功能默认是不激活的,实际上这取决于隐藏参数_KKS_USE_MUTEX_PIN,在10.2.0.2之后_KKS_USE_MUTEX_PIN默认为TRUE。 换而言之在版本10.2中我们还是可以关闭KKS使用MUTEX替代PIN保护CURSOR的, 但是在版本11g中则几乎无法关闭MUTEX。 注意10.2中仅当KKS真正使用MUTEX时,library cache pin不再用作cursor pin。
  • 基于对不同的游标统计信息的操作有不同的等待事件:
    • 为执行某个SQL而PIN一个游标Cursor                        ==>Cursor: Pin S Wait on X
    • 当执行一个游标而PIN Cursor,而该Cursor正被其他进程以S mode检测             ==>  cursor:pin S
  • 当试图重建一个游标Cursor  ==> Cursor: pin X  该等待事件一般不太会看到,因为当一个游标正被执行,且其需要重建时会有另一个游标被创建
  • 保护游标的mutex嵌入在游标结构内
  • Mutex类型为’Cursor Pin’ (kgx_kks3)
  • 等待事件均为 ‘cursor: pin *’的形式

 

 KKS使用MUTEX情况下SQL语句的 解析与执行的收益

 

在版本10.2中, 以下是几个SQL解析与执行从MUTEX哪里获得主要收益:

  • 在某个父游标下构建一个新的子游标
    • 首先这种构建新子游标的操作更廉价了,  当时Maclean仍要告诫你 一个父游标下过多的子游标仍不是一件好事情
  • 对父游标的检测
    •  在找到一个合适的游标并执行前,父游标需要被适当检测。 对父游标的这种检测目前也使用mutex来保护了,所以这种检测更的成本更低了
  • 对于已经加载在Library Cache 中的SQL语句重复执行
    • 常规情况下,当一个进程要执行SQL游标前总是必须要先pin它
    • 不使用MUTEX的情况:若游标处于OPEN状态下以便今后的重复执行,且参数cursor_space_for_time(CSFT  目前已不推荐使用该参数)为TRUE,则每一次重复执行可以不需要library cache pin。 若游标处于OPEN状态下但是cursor_space_for_time=false,则进程在重复执行SQL游标前总是要先拿library cache pin
    • 使用MUTEX的情况: 相反,若使用mutex来替代library cache pin时,则无需关心cursor_space_for_time 。 仅第一个进程需要做一个PIN,其他后续进程都只需要简单地在对应保护cursor heap的mutex上拿一个共享reference 。

 

查询SQL统计信息

通过V$SQLSTAT视图(本质上是X$KKSSQLSTAT)访问SQL统计信息时,其所需要的CPu和获取的Latch数量要远远少于访问其他V$SQL视图。 在早期版本中, 并行地访问V$SQL或者V$SQLAREA视图会造成 library cache latch的争用。

 

 

 

Mutex 的统计信息

下面是一个AWR中的Mutex Sleep Statistics, 这些数据主要来源于V$MUTEX_SLEEP视图。

 

Mutex Sleep Summary

  • ordered by number of sleeps desc
Mutex Type Location Sleeps Wait Time (ms)
Library Cache kglhdgh1 64 2,356 0
Library Cache kglpnal2 91 2,345 0
Cursor Pin kkslce [KKSCHLPIN2] 2,084 0
Library Cache kglpin1 4 956 0
Library Cache kglhdgn2 106 784 0
Library Cache kglpndl1 95 691 0
Library Cache kglpnal1 90 605 0
Library Cache kgllkdl1 85 580 0
Library Cache kgllkal1 80 404 0
Library Cache kglllal3 111 282 0
Library Cache kglllal1 109 218 0
Library Cache kglhdgn1 62 163 0
Library Cache kgllldl2 112 156 0
Library Cache kgllkc1 57 105 0
Library Cache kglget2 2 100 0
Library Cache kglini1 32 53 0
Library Cache kglget1 1 31 0
Cursor Pin kksLockDelete [KKSCHLPIN6] 22 0
Library Cache kgllkal3 82 18 0
Library Cache kglUnsetHandleReference 120 10 0
Cursor Pin kksxsccmp [KKSCHLPIN5] 10 0
Library Cache kglobld1 75 8 0
Cursor Pin kksfbc [KKSCHLPIN1] 8 0
Library Cache kglUpgradeLock 119 7 0
Library Cache kglhdgc1 102 2 0
Cursor Pin kksfbc [KKSCHLFSP2] 2 0
Library Cache kgldtin1 42 1 0
Library Cache kglhbh1 63 1 0
Library Cache kgllkal5 84 1 0
Library Cache kglrdtin1 44 1 0
Cursor Parent kkscsPruneChild [KKSPRTLOC35] 1 0

 

Mutex的类型Mutex Type

Mutex的类型其实就是 mutex对应的客户的名字,  在版本10.2中基本只有KKS使用Mutex,所以仅有3种:

  • Cursor Stat (kgx_kks1)
  • Cursor Parent (kgx_kks2)
  • Cursor Pin (kgx_kks3)

 

在版本11g中扩展了对Mutex的使用,在Library Cache的HASH BUCKET中嵌入了mutex以保护hash bucket,所以多了一种mutex type : Library Cache

 

 

哪些代码函数会申请Mutex?

 

 

Oracle中哪些代码函数会申请Mutex? 例如KKSFBC等,其实很像 V$LATCH_MISSES 的location列

10.2中最常见的下面的几个函数

kkspsc0  -负责解析游标 – 检测我们正在解析的游标是否有对象的parent cursor heap 0存在

kksfbc           –  负责找到合适的子游标 或者创建一个新的子游标

kksFindCursorstat  

 

11g开始有大量函数需要用到Mutex了

SQL> select location from X$MUTEX_SLEEP_HISTORY;

LOCATION
—————————————-
kkslce [KKSCHLPIN2]
kksfbc [KKSCHLFSP2]
kglhdgn2 106
kglpin1 4
kglhdgn2 106
kglllal1 109
kglpin1 4
kglpndl1 95
kglpin1 4
kglpin1 4
kksfbc [KKSCHLFSP2]
kglhdgn1 62
kglpnal1 90
kglllal3 111
kglpnal1 90
kglpnal1 90
kglget2 2
kglllal3 111
kglget2 2
kglobld1 75
kkslce [KKSCHLPIN2]
kglpndl1 95
kglpndl1 95
kglpin1 4
kkslce [KKSCHLPIN2]
kglpin1 4
kglget2 2
kglllal1 109
kgllkc1 57
kglget2 2
kglpnal1 90
kglpin1 4
kglpin1 4
kglpin1 4
kgllkdl1 85
kglllal3 111
kgllldl2 112
kglpin1 4
kglpndl1 95
kkslce [KKSCHLPIN2]
kksLockDelete [KKSCHLPIN6]
kglpndl1 95
kkslce [KKSCHLPIN2]
kglpnal1 90
kglpin1 4
kglpin1 4
kgllldl2 112
kgllkdl1 85
kglpin1 4
kglhdgn2 106
kglhdgn2 106
kksLockDelete [KKSCHLPIN6]
kglhdgn1 62

 

 

Mutex的Get和Sleep

当一个Mutex被申请时, 一般称为一个get request。 若初始的申请未能得到授权, 则该进程会因为此次申请而进入到255次SPIN中(_mutex_spin_count Mutex spin count),每次SPIN循环迭代过程中该进程都会去看看Mutex被释放了吗。

若该Mutex在SPIN之后仍未被释放,则该进程针对申请的mutex进入对应的mutex wait等待事件中。 实际进程的等待事件和等待方式由mutex的类型锁决定,例如 Cursor pin、Cursor Parent。  举例来说,这种等待可能是阻塞等待,也可以是sleep。

但是请注意在V$MUTEX_SLEEP_*视图上的sleep列意味着等待的次数。相关代码函数在开始进入等待时自加这个sleep字段。

等待计时从进程进入等待前开始计算等待时间, 当一个进程结束其等待,则等待的时间加入都总和total中。  该进程再次尝试申请之前的Mutex,若该Mutex仍不可用,则它再次进入spin/wait的循环。

 

V$MUTEX_SLEEP_HISTORY视图的GETS列仅在成功申请到一个Mutex时才增加。

 

短期持有一个mutex:  spin 循环255次一般可以有效以S mode获得一个mutex, 前提是该Mutex 已经被以S mode持有。 简单来说若有2个进程同时以S mode去申请一个Mutex,则稍晚的一个申请者需要进入SPIN并等稍早一点的申请者完成它的例如创建针对该mutex的一个reference的操作,但这都是非常迅速的操作。

 

长期持有一个Mutex: 如若一个Mutex已经被某进程以X mode持有, 则往往有其进程以SHRD模式去申请该mutex时仍发现该mutex 以X mode被其他进程所持有,则往往这个EXCL  持有是 LONG_EXCL(可以通过SSD DUMP发现),则后续的申请者往往要进入spin循环,甚至需要等待

 

上面我讲了willing-to-wait的mutex, 实际上mutex 的申请也可以是 nowait的。进程以nowait申请mutex时不会进入spin-cycle也不sleep,它只继续常规处理。 当一个nowait get失败时,将增加一次miss,但是实际上V$MUTEX_SLEEP_*中记录的miss不是这样的miss, 视图中记录的miss是等待的次数, 对于真正的miss没有统计项。

 

Wait Time等待时间

类似于latch,spin time 不算做mutex的消耗时间,它只包含等待消耗的时间。

 

真正理解Mutex相关的等待

 

Mutex数据结构中存放了Holder id持有者ID , Ref Count,和其他Mutex相关的统计信息。 Holder id对应于持有该Mutex的session id (v$session.sid)  。 特别注意, Ref Count是进程并发以S mode参考该Mutex的进程数量(如下文的演示)。

当一个Mutex被以X mode 持有,则Holder id 为对应持有该mutex的session id,而Ref Count为0。

 

每一个共享S mode持有者仅仅增加mutex上的Ref Count。 可供大量session并发以S mode持有参考一个Mutex。 但是注意更新ref count的操作是串行的, 这是为了避免错漏并维护mutex中正确的ref count。

 

下面我们详细介绍一个执行游标过程中对mutex share pin的过程:

  • 某进程以SHRD 模式申请一个Mutex,并尝试临时修改该Mutex的Holder ID
  • 若该Mutex正被他人更新,则该session会将Holder id设置为本session的sid,之后该进程将增加ref count,之后再清楚mutex上的Holder id。简单来说 这个Holder id是真正做了并行控制的功能。 若该Holder id 被设置了,则说明该Mutex要么被以EXCL模式持有,要么正有一个其他进程在以S mode申请该Mutex的过程中(例如更新Ref Count)。 当更新Ref Count时临时设置holder id的目的就是为了实现避免其他进程并发更新该Mutex的机制。 通过这些例子说明了 , Mutex既可以用作Latch并发控制, 也可用作pin。
  • 若Holder id已被设置,则申请进程将可能进入等待事件 。  例如若当前Mutex的持有者进程正以X mode更新该Mutex,则申请者的等待事件应为”cursor: pin S on X” 。  而若当持有者Holder并不是”真的要持有” 该Mutex,而仅仅是尝试更新其Ref Count,则第二个进程将等在’ Cursor :pin S’等待事件上; 实际正在更新Ref count的操作时很快的,是一种轻微的操作。 当第一个进程正在更新mutex,则后续的申请进程将进入spin 循环中255次等待前者结束。 当mutex上不再有 Holder id时(如前者的进程已经更新完Ref Count)时, 则申请者进程将Holder ID设为自身的SID,并更新Ref Count,并清除Holder id。 若在255次循环SPIN后mutex仍不被释放,则该进程进入等待并不再跑在CPU上。

 

 

 Mutex相关的等待事件

 

cursor: mutex * events等待事件

  • cursor: mutex * events等待事件用于Cursor Parent 和 Cursor stats类型的操作:
    • ‘cursor: mutex X’ , 某个进程申请以EXCL mode持有mutex时进入该等待, 该Mutex要么正被其他进程以SHRD模式参考,这导致X mode的申请必须要等待直到Ref count=0,  或者该mutex正被另一个进程以X mode持有。
    • 相关操作要求以EXCL X mode持有Mutex的:
      • 在一个父游标下创建一个新的子游标
      • 捕获SQL中的绑定变量
      • 更新或构件SQL统计信息V$SQLSTATS
  • ‘Cursor: Mutex S’ , 某个进程以SHRD S mode申请一个Mutex, 而该Mutex要么被其他进程已EXCL X mode所持有,要么其他进程正在更新mutex 上的Ref Count。
    • 相关类型的操作一般是检测父游标或者CURSOR统计信息数据, 此外查询V$SQLSTATS也会造成CURSOR statistics被查询

 

 

 

‘cursor: pin * events’等待事件

该类等待事件一般是为了pin相关的子游标

  • cursor: pin S  当一个进程以共享pin模式申请一个Mutex,而不能立即获得时,进入cursor: Pin S等待事件。 Mutex Pin是以共享类型的操作,例如执行一个游标。
    • 当一个进程等在cursor: pin S上,说明该进程在对一个共享的mutex pin 参考或取消参考时,有其他的进程也正在为同样的cursor heap创建或者取消一个共享Mutex pin。 实际上cursor: pin S 等待事件应当很少见,因为更新共享Mutex pin 的reference 应当是很快的。 再重复一次,S mode的Mutex可以被并发持有, 但是更新Mutex的Ref Count仍需要串行地处理 。 一旦reference count被增加好,则后续进程将可以为同样的cursor heap增加reference count。 因此此处mutex 即可以扮演Latch的角色(串行控制ref count的更新),又可以扮演pin的角色(ref count本身)。
  • ‘cursor: pin X’  当一个进程需要以EXCL X mode获得mutex时, 这类需要EXCL X 模式的串行操作包括:
    • 构建一个子游标
    • 某个进程已经以X mode持有该Mutex
    • 一个或多个进程正在reference 该Mutex (shared mutex pin)
  • ‘Cursor: pin S on X’ 最常见的等待事件,  进程为了共享操作例如执行pin游标而以SHRD S mode申请mutex, 但是未立即获得。原因是该游标被其他进程以EXCL X mode 持有了。

 

 Mutex的相关统计视图

 

V$MUTEX_SLEEP

 

shows the wait time, and the number of sleeps for each combination of mutex type and location.

 

Column Datatype Description
MUTEX_TYPE VARCHAR2(32) Type of action/object the mutex protects
LOCATION VARCHAR2(40) The code location where the waiter slept for the mutex
SLEEPS NUMBER Number of sleeps for this MUTEX_TYPE and LOCATION
WAIT_TIME NUMBER Wait time in microseconds

 

 

V$MUTEX_SLEEP_HISTORY

 

displays time-series data. Each row in this view is for a specific time, mutex type, location, requesting session and blocking session combination. That is, it shows data related to a specific session (requesting session) that slept while requesting a specific mutex type and location, because it was being held by a specific blocking session. The data in this view is contained within a circular buffer, with the most recent sleeps shown.

 

Column Datatype Description
SLEEP_TIMESTAMP TIMESTAMP(6) The last date/time this MUTEX_TYPE and LOCATION was slept for by theREQUESTING_SESSION, while being held by the BLOCKING_SESSION.
MUTEX_TYPE VARCHAR2(32) Type of action/object the mutex protects
GETS NUMBER The number of times the mutex/location was requested by the requesting session while being held by the blocking session. GETS is only incremented once per request, irrespective of the number of sleeps required to obtain the mutex.
SLEEPS NUMBER The number of times the requestor had to sleep before obtaining the mutex
REQUESTING_SESSION NUMBER The SID of a session requesting the mutex
BLOCKING_SESSION NUMBER The SID of a session holding the mutex
LOCATION VARCHAR2(40) The code location where the waiter slept for the mutex
MUTEX_VALUE RAW(4) If the mutex is held in exclusive (X) mode, this column shows the SID of the blocking session, else it shows the number of sessions referencing the mutex in S mode.
P1 NUMBER Internal use only
P1RAW RAW(4) Internal use only
P2 NUMBER Internal use only
P3 NUMBER Internal use only
P4 NUMBER Internal use only
P5 VARCHAR2(64) Internal use only

接着我们会在环境中模拟cursor pin S wait on X的场景,并通过systemstate dump和v$mutex_sleep , v$mutex_sleep_history等视图观察这一现象

 
session A:
SQL> select * from v$version;

BANNER
—————————————————————-
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 – 64bi
PL/SQL Release 10.2.0.5.0 – Production
CORE 10.2.0.5.0 Production
TNS for Linux: Version 10.2.0.5.0 – Production
NLSRTL Version 10.2.0.5.0 – Production
www.askmac.cn

SQL> show parameter kks
SQL>
SQL> create table mac_kks tablespace users nologging as select * from dba_objects;

Table created.

SQL> insert /*+ append */ into mac_kks select * from mac_kks;

77386 rows created.

SQL> commit;

Commit complete.

SQL> insert /*+ append */ into mac_kks select * from mac_kks;

154772 rows created.

SQL> commit;

Commit complete.

SQL> insert /*+ append */ into mac_kks select * from mac_kks;

309544 rows created.

SQL> commit;

Commit complete.

SQL> insert /*+ append */ into mac_kks select * from mac_kks;

619088 rows created.

SQL> commit;

Commit complete.

SQL> insert /*+ append */ into mac_kks select * from mac_kks;

1238176 rows created.

SQL> commit;

Commit complete.

 

 
SQL> oradebug setmypid
Statement processed.
SQL> oradebug tracefile_name
Statement processed.
SQL> alter table mac_kks add t2 char(2000) default ‘MACLEAN’;

 

 

session B:
SQL> oradebug setospid 32424
Oracle pid: 17, Unix process pid: 32424, image: oracle@vrh8.oracle.com (TNS V1-V3)
SQL> oradebug suspend;
Statement processed.

 

session C:

select * from mac_kks where rownum=1; ==> hang

session D:

select * from mac_kks where rownum=1; ==> hang

 

session E:

SQL> select sid,event from v$session where wait_class!=’Idle’;

SID EVENT
———- —————————————————————-
141 SQL*Net message to client
145 library cache lock
149 cursor: pin S wait on X
159 log buffer space

SQL> oradebug setmypid
Statement processed.

SQL> oradebug dump systemstate 266;
Statement processed.

SQL> oradebug tracefile_name
/s01/admin/G10R25/udump/g10r25_ora_32537.trc
Object Names
~~~~~~~~~~~~
LOCK: handle=a7115ef0
Mutex 7fff7abadecf

 

KGX Atomic Operation Log 0x8d88a8d8
Mutex 0x954eaff8(145, 0) idn 7fff7abadecf oper EXCL
Cursor Pin uid 145 efd 0 whr 1 slp 0
opr=3 pso=0x97951af0 flg=0
pcs=0x954eaff8 nxt=(nil) flg=35 cld=0 hd=0xa7864b08 par=0x9523a9e0
ct=0 hsh=0 unp=(nil) unn=0 hvl=9595c3d8 nhv=1 ses=0xa8416738
hep=0x954eb078 flg=80 ld=1 ob=0x95ac6128 ptr=0x8fd90128 fex=0x8fd8f438

0x954eaff8(145, 0) ==> sid和 ref count
pso ==> parent state object
hd=0xa7864b08 ==>cursor 对应的handle address
par ==> 父游标的heap 0 pointer
ses=0xa8416738 ==》 一般 EXCL是才有 session address v$session.saddr

SID=145 对Mutex 0x954eaff8 oper EXCL以X mode Hold 该Mutex, SID=145 在等 SYS.MAC_KKS表的library cache lock,该表被X mode pin和lock,而解析SQL要求以S mode lock该表

SID=149 对Mutex 0x954eaff8 申请 oper GET_SHRD, SID=149在等cursor: pin S wait on X

KGX Atomic Operation Log 0x8db79798
Mutex 0x954eaff8(145, 0) idn 7fff7abadecf oper GET_SHRD
Cursor Pin uid 149 efd 0 whr 5 slp 13893
opr=2 pso=0x8e6bd518 flg=0
pcs=0x954eaff8 nxt=(nil) flg=35 cld=0 hd=0xa7864b08 par=0x9523a9e0
ct=0 hsh=0 unp=(nil) unn=0 hvl=9595c3d8 nhv=1 ses=0xa8416738
hep=0x954eb078 flg=80 ld=1 ob=0x95ac6128 ptr=0x8fd90128 fex=0x8fd8f438

SO: 0xa841bd18, type: 4, owner: 0xa830cf98, flag: INIT/-/-/0x00
(session) sid: 149 trans: (nil), creator: 0xa830cf98, flag: (80000041) USR/- BSY/-/-/-/-/-
DID: 0001-0019-00000066, short-term DID: 0000-0000-00000000
txn branch: (nil)
oct: 0, prv: 0, sql: 0x8d88bf90, psql: (nil), user: 0/SYS
service name: SYS$USERS
O/S info: user: oracle, term: pts/5, ospid: 32510, machine: vrh8.oracle.com
program: sqlplus@vrh8.oracle.com (TNS V1-V3)
application name: sqlplus@vrh8.oracle.com (TNS V1-V3), hash value=543908804
waiting for ‘cursor: pin S wait on X’ wait_time=0, seconds since wait started=0
idn=7abadecf, value=9100000000, where|sleeps=500003645
blocking sess=0x(nil) seq=13901
Dumping Session Wait History
for ‘cursor: pin S wait on X’ count=1 wait_time=0.266596 sec
idn=7abadecf, value=9100000000, where|sleeps=500003644
for ‘cursor: pin S wait on X’ count=1 wait_time=0.010679 sec
idn=7abadecf, value=9100000000, where|sleeps=500003643
for ‘cursor: pin S wait on X’ count=1 wait_time=0.010633 sec
idn=7abadecf, value=9100000000, where|sleeps=500003642
for ‘cursor: pin S wait on X’ count=1 wait_time=0.010843 sec
idn=7abadecf, value=9100000000, where|sleeps=500003641
for ‘cursor: pin S wait on X’ count=1 wait_time=0.011008 sec
idn=7abadecf, value=9100000000, where|sleeps=500003640
for ‘cursor: pin S wait on X’ count=1 wait_time=0.010406 sec

 

 

SO: 0xa8416738, type: 4, owner: 0xa830bfa8, flag: INIT/-/-/0x00
(session) sid: 145 trans: (nil), creator: 0xa830bfa8, flag: (80000041) USR/- BSY/-/-/-/-/-
DID: 0001-0017-0000008E, short-term DID: 0000-0000-00000000
txn branch: (nil)
oct: 3, prv: 0, sql: 0x8d88bf90, psql: (nil), user: 0/SYS
service name: SYS$USERS
O/S info: user: oracle, term: pts/4, ospid: 32485, machine: vrh8.oracle.com
program: sqlplus@vrh8.oracle.com (TNS V1-V3)
application name: sqlplus@vrh8.oracle.com (TNS V1-V3), hash value=543908804
waiting for ‘library cache lock’ wait_time=0, seconds since wait started=165
handle address=a7115ef0, lock address=978def20, 100*mode+namespace=c9
blocking sess=0x(nil) seq=9
Dumping Session Wait History
for ‘library cache lock’ count=1 wait_time=3.297287 sec
handle address=a7115ef0, lock address=978def20, 100*mode+namespace=c9
for ‘library cache lock’ count=1 wait_time=2.930321 sec
handle address=a7115ef0, lock address=978def20, 100*mode+namespace=c9
for ‘library cache lock’ count=1 wait_time=2.930856 sec
handle address=a7115ef0, lock address=978def20, 100*mode+namespace=c9
for ‘library cache lock’ count=1 wait_time=2.930698 sec
handle address=a7115ef0, lock address=978def20, 100*mode+namespace=c9
for ‘library cache lock’ count=1 wait_time=2.931518 sec
handle address=a7115ef0, lock address=978def20, 100*mode+namespace=c9

SO: 0x978def20, type: 53, owner: 0xa8456058, flag: INIT/-/-/0x00
LIBRARY OBJECT LOCK: lock=978def20 handle=a7115ef0 request=S
call pin=(nil) session pin=(nil) hpc=0005 hlc=0000
htl=0x978defa0[0x8e4a8950,0x8e4a8950] htb=0x8e4a8950 ssga=0x8e4a7a88
user=a8416738 session=a8416738 count=0 flags=RES/[0010] savepoint=0x1f
LIBRARY OBJECT HANDLE: handle=a7115ef0 mtx=0xa7116020(0) lct=8 pct=10 cdp=0
name=SYS.MAC_KKS
hash=c066b9b9c6c80736a15f5ba325563fdb timestamp=04-14-2013 00:43:14
namespace=TABL flags=KGHP/TIM/SML/[02000000]
kkkk-dddd-llll=0000-0701-0201 lock=X pin=X latch#=3 hpc=0006 hlc=0004
lwt=0xa7115f98[0x978def50,0x978def50] ltm=0xa7115fa8[0xa7115fa8,0xa7115fa8]
pwt=0xa7115f60[0xa7115f60,0xa7115f60] ptm=0xa7115f70[0xa7115f70,0xa7115f70]
ref=0xa7115fc8[0xa7115fc8,0xa7115fc8] lnd=0xa7115fe0[0x9cc2c260,0xa79fd198]
LIBRARY OBJECT: object=95f0e5b0
type=TABL flags=EXS/LOC/UPD[0905] pflags=[0000] status=VALD load=0
DATA BLOCKS:
data# heap pointer status pins change whr
—– ——– ——– ——— —- —— —
0 a78d3840 95f0e708 I/P/A/-/- 0 NONE 00
8 957a6ad8 94461f88 I/P/A/-/- 1 UPDATE 00

Mutex 0x954eaff8 被 SID=145 oper EXCL以X mode Hold 该Mutex

体现为 子游标child cursor被以 X mode pin
SO: 0x8e6bd518, type: 53, owner: 0xa841bd18, flag: INIT/-/-/0x00
LIBRARY OBJECT LOCK: lock=8e6bd518 handle=a7864b08 mode=N
call pin=(nil) session pin=(nil) hpc=0000 hlc=0000
htl=0x8e6bd598[0x8e542f60,0x979da4d0] htb=0x979da4d0 ssga=0x979d96c8
user=a841bd18 session=a841bd18 count=1 flags=CBK[0020] savepoint=0x0
LIBRARY OBJECT HANDLE: handle=a7864b08 mtx=0xa7864c38(0) lct=2 pct=3 cdp=0
namespace=CRSR flags=RON/KGHP/PN0/EXP/[10010100]
kkkk-dddd-llll=0000-0001-0001 lock=N pin=X latch#=3 hpc=0002 hlc=0002
lwt=0xa7864bb0[0xa7864bb0,0xa7864bb0] ltm=0xa7864bc0[0xa7864bc0,0xa7864bc0]
pwt=0xa7864b78[0xa7864b78,0xa7864b78] ptm=0xa7864b88[0xa7864b88,0xa7864b88]
ref=0xa7864be0[0x954eb320,0x954eb320] lnd=0xa7864bf8[0xa7864bf8,0xa7864bf8]
LIBRARY OBJECT: object=95ac6128
type=CRSR flags=EXS[0001] pflags=[0000] status=VALD load=0
DATA BLOCKS:
data# heap pointer status pins change whr
—– ——– ——– ——— —- —— —
0 8d81aa70 95ac6240 I/P/A/-/- 0 NONE 00
6 954eb0f0 8fd90128 I/P/A/-/E 0 NONE 00

并行执行6个SQL语句 并做systemstate dump :

 

[oracle@vrh8 ~]$ grep SHRD /s01/admin/G10R25/udump/g10r25_ora_32716.trc |grep 0x95987388
Mutex 0x95987388(0, 6) idn 7fff8f32fff6 oper SHRD
Mutex 0x95987388(0, 6) idn 7fff8f32fff6 oper SHRD
Mutex 0x95987388(0, 6) idn 7fff8f32fff6 oper SHRD
Mutex 0x95987388(0, 6) idn 7fff8f32fff6 oper SHRD
Mutex 0x95987388(0, 6) idn 7fff8f32fff6 oper SHRD
Mutex 0x95987388(0, 6) idn 7fff8f32fff6 oper SHRD

 

 

to be continued ……………………………

kksfbc Clear parse

如果在udump/bdump目录下trace中找到

 

kksfbc : Clear parse Err=44201 xsc=1105fddf0 chd=0 clk=0 400020 40000000 20a2000

kksfbc : Clear parse Err=44201 xsc=1113ad1f8 chd=0 clk=0 400020 40000000 20a2000

的相关信息,一般是可以忽略的。

 

kksfbc是解析parse 过程中的重要函数,其作用在于find bound cursor、并返回一个合适的子游标, 而相关的内核函数kkspfx则用于pin cursor以便执行。

 

kksfbc – Find Bound Cursor – creation of a new child cursor

 

Oracle 中硬解析(Hard Parse)构造子游标 build child cursor的典型stack call:

opitca()<-kksFullTypeCheck()<-rpiswu2()<-kksLoadChild()<-kkslod()<-kglobld()<-kglobpn()<-kglpim()<-kglpin()<-kxsRuntimeLock()<-kksfbc()<-kkspsc0()<-kksParseCursor()<-opiosq0()<-kpooprx()<kpoal8()<-opiodr()<-ttcpip()<-opitsk()<-opiino()<-opiodr()<-opidrv()<-sou2o()<-opimai_real() <-main()

 

软解析找寻一个合适子游标的典型stack call:

kksSearchChildList ()<-kksfbc()<-kkspsc0()<-kksParseCursor()<-opiosq0()<-kpooprx()<kpoal8()<-opiodr()<-ttcpip()<-opitsk()<-opiino()<-opiodr()<-opidrv()<-sou2o()<-opimai_real() <-main()

 

执行时找到一个合适子游标的典型stack call:

kksSearchChildList()<-kksfbc()<-opiexe ()<-kpoal8 ()<-opiodr ()<-ttcpip ()<-opitsk ()<-opiino ()<-opiodr ()<-opidrv ()<-sou2o ()<-opimai_real main ()

 

 

 

 

	
Continously Receiving "Kksfbc : Clear Parse Err=44201" In Bdump Tracefiles (Doc ID 748940.1)
Modified01-MAR-2013TypePROBLEMStatusMODERATED(EXTERNAL)Priority3To Bottom
In this Document
  Symptoms
  Cause
  Solution
  References

Platforms: 1-914CU;

This document is being delivered to you via Oracle Support's Rapid Visibility (RaV) process and therefore has not been subject to an independent technical review.
Applies to:

Oracle Server - Enterprise Edition - Version: 10.2.0.3 and later   [Release: 10.2 and later ]
Information in this document applies to any platform.
Symptoms

-- Problem Statement:
On 10.2.0.3 in Production:
we are getting many trace files generated in bdump with the following message :

ERROR
kksfbc : Clear parse Err=44201 xsc=800003ffbfc3d678 chd=0000000000000000 clk=0000000000000000
400020 0 22080

Cause

This issue due to Bug 5397402 "KKSFBC : CLEAR PARSE ERR=44201 XSC=0XB5F48550 CHD=(NIL) CLK=(NIL)

Diagnostic output may be written to the session's trace file under some conditions. The extraneous
trace looks like this:

kksfbc : Clear parse Err=44201 xsc=800003ffbfc3d678 chd=0000000000000000 clk=0000000000000000 400020 0 22080

Solution

-- To implement the solution, please execute the following steps::

1) Apply Patch 5397402 

Or

2) Apply the 10.2.0.5 patchset.

Also you can safely ignore the generated trace files.
References

BUG:5397402 - KKSFBC : CLEAR PARSE ERR=44201 XSC=0XB5F48550 CHD=(NIL) CLK=(NIL) IN TRACES

了解你所不知道的SMON功能(十二):Shrink UNDO(rollback) SEGMENT

SMON对于Undo(Rollback)segment的日常管理还不止于OFFLINE UNDO SEGMENT ,在AUM(automatic undo management或称SMU)模式下SMON还定期地收缩Shrink Rollback/undo segment。

 

触发场景

 

这种AUM下rollback/undo segment的undo extents被shrink的现象可能被多种条件触发:

  • 当另一个回滚段的transaction table急需undo空间时
  • 当SMON定期执行undo/rollback管理时(每12个小时一次):
    • SMON会从空闲的undo segment中回收undo space,以便保证其他tranaction table需要空间时可用。另一个好处是undo datafile的身材不会急速膨胀导致用户要去resize
    • 当处于undo space空间压力时,特别是在发生UNDO STEAL的条件下; SGA中会记录前台进程因为undo space压力而做的undo steal的次数(v$undostat UNXPSTEALCNT EXPSTEALCNT);若这种UNDO STEAL的次数超过特定的阀值,则SMON会尝试shrink transaction table

若smon shrink rollback/undo真的发生时,会这样处理:

计算平均的undo retention大小,按照下列公式:

retention size=(undo_retention * undo_rate)/(#online_transaction_table_segment 在线回滚段的个数)

对于每一个undo segment

  • 若是offline的undo segment,则回收其所有的已过期expired undo extents,保持最小2个extents的空间
  • 若是online的undo segment,则回收其所有的已过期expired undo extents,但是保持其segment所占空间不小于平均retention对应的大小。

 

注意SMON的定期Shrink,每12个小时才发生一次,具体发生时可以参考SMON进程的TRACE。

 

若系统中存在大事务,则rollback/undo segment可能扩展到很大的尺寸;视乎事务的大小,则undo tablespace上的undo/rollback segment会呈现出不规则的空间占用分布。

SMON的定期清理undo/rollback segment就是要像一个大锤敲击钢铁那样,把这些大小不规则的online segment清理成大小统一的回滚段,以便今后使用。

当然这种定期的shrink也可能造成一些阻碍,毕竟在shrink过程中会将undo segment header锁住,则事务极低概率可能遇到ORA-1551错误:

 

 

[oracle@vmac1 ~]$ oerr ora 1551
01551, 00000, "extended rollback segment, pinned blocks released"
// *Cause: Doing recursive extent of rollback segment, trapped internally
//        by the system
// *Action: None

 

如何禁止SMON SHRINK UNDO SEGMENT?

 

可以通过设置诊断事件event=’10512 trace name context forever, level 1’来禁用SMON OFFLINE UNDO SEGS;

 

SQL> select * from global_name;

GLOBAL_NAME
--------------------------------------------------------------------------------
www.askmac.cn

SQL> alter system set events '10512 trace name context forever,level 1';

System altered.

 

 

相关BUG

这些BUG主要集中在9.2.0.8之前,10.2.0.3以后几乎绝迹了:

 

Bug 1955307 – SMON may self-deadlock (ORA-60) shrinking a rollback segment in SMU mode [ID 1955307.8]
Bug 3476871 : SMON ORA-60 ORA-474 ORA-601 AND DATABASE CRASHED
Bug 5902053 : SMON WAITING ON ‘UNDO SEGMENT TX SLOT’ HANGS DATABASE
Bug 6084112 : INSTANCE SLOW SHOW SEVERAL LONGTIME RUNNING WAIT EVENTS

了解Database Replay Capture内部原理

Database Replay是11g中很酷的特性,对于workload capture的内部工作原理大家理解的不多,这里就介绍一下。

对于Workload Capture需要考虑的因素:

  • 负载捕获文件消耗定量的磁盘空间,这些捕获文件是2进程文件,无法直接阅读,有测试表明在大并发量的OLTP环境中可以达到capture 10分钟占用1G磁盘空间
  • 数据库重启:
    • 可能是保证捕获所有事务的可靠重放的唯一路径
      • 使用startup restrict启动实例,避免不完整的事务捕获
      • 启动capture会取消restrict模式
    • 基于负载类型重启不是必要的
  • 为重放目的恢复数据库的多种方法:
    • 基于scn或时间的物理恢复
    • 逻辑恢复应用数据
    • 闪回或者快照数据
  • Capture可以指定过滤器作为捕获workload子集的方法
  • 需要的权限包括SYSDBA、SYSOPER和合适的OS权限
  • 性能消耗:
    • 在TPCC测试中capture的性能损耗为4.5%
    • 对于每个session会多消耗64KB的内存
    • 必要的Workload Capture耗费文件系统磁盘空间

 

 

这里需要注意的有2点, 对于RAC集群存在workload capture  file的目录必须位于共享文件系统上,否则start_capture时会报错。

对于每个session会多消耗64KB的内存,这是由于本质上写出负载信息到workload  capture file的同样是Server Process服务进程本身,但是这种写出并非在parse解析或execution执行阶段,Server Process将其LOGON、LOGOFF、SQL执行等信息记录存放在PGA中,主要是WCR Capture PG、WCR Capture PGA中,当PGA中的工作负载历史记录达到一定数目时,Server Process本身负责写出这些数据到WCR文件中,在写出这些WCR文件时Server Process进入’WCR: capture file IO write’等待事件。

与WCR相关的等待事件另有:

 

SQL> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE    11.2.0.3.0      Production
TNS for Linux: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production

SQL> select name  from v$event_name where name like '%WCR%';

NAME
----------------------------------------------------------------
WCR: replay client notify
WCR: replay clock
WCR: replay lock order
WCR: replay paused
WCR: RAC message context busy
WCR: capture file IO write
WCR: Sync context busy
latch: WCR: sync
latch: WCR: processes HT

11g中还多出了部分为WCR而设的LATCH 

  1* select name,gets from v$latch where name like '%WCR%'
SQL> /

NAME                                 GETS
------------------------------ ----------
WCR: kecu cas mem                       3
WCR: kecr File Count                   37
WCR: MMON Create dir                    1
WCR: ticker cache                       0
WCR: sync                             495
WCR: processes HT                       0
WCR: MTS VC queue                       0

7 rows selected.

 

 

我们通过如下演示具体说明Database Replay Capture的内部原理

 

 

1. 首先打开capture dbms_workload_capture.start_capture

 CREATE OR REPLACE DIRECTORY dbcapture AS '/home/oracle/dbcapture';

execute dbms_workload_capture.start_capture('CAPTURE','DBCAPTURE',default_action=>'INCLUDE');

SQL> select id,name,status,start_time,end_time,connects,user_calls,dir_path from 
dba_workload_captures where id = (select max(id) from dba_workload_captures) ;

        ID
----------
NAME
--------------------------------------------------------------------------------
STATUS                                   START_TIM END_TIME    CONNECTS
---------------------------------------- --------- --------- ----------
USER_CALLS
----------
DIR_PATH
--------------------------------------------------------------------------------
         1
CAPTURE
IN PROGRESS                              08-DEC-12                   11

        ID
----------
NAME
--------------------------------------------------------------------------------
STATUS                                   START_TIM END_TIME    CONNECTS
---------------------------------------- --------- --------- ----------
USER_CALLS
----------
DIR_PATH
--------------------------------------------------------------------------------
       167
/home/oracle/dbcapture

2. 窥探 capture file目录

[oracle@mlab2 dbcapture]$ ls -lR
.:
total 8
drwxr-xr-x 2 oracle oinstall 4096 Dec  8 07:24 cap
drwxr-xr-x 3 oracle oinstall 4096 Dec  8 07:24 capfiles
-rw-r--r-- 1 oracle oinstall    0 Dec  8 07:24 wcr_cap_00001.start

./cap:
total 4
-rw-r--r-- 1 oracle oinstall 91 Dec  8 07:24 wcr_scapture.wmd

./capfiles:
total 4
drwxr-xr-x 12 oracle oinstall 4096 Dec  8 07:24 inst1

./capfiles/inst1:
total 40
drwxr-xr-x 2 oracle oinstall 4096 Dec  8 08:31 aa
drwxr-xr-x 2 oracle oinstall 4096 Dec  8 07:24 ab
drwxr-xr-x 2 oracle oinstall 4096 Dec  8 07:24 ac
drwxr-xr-x 2 oracle oinstall 4096 Dec  8 07:24 ad
drwxr-xr-x 2 oracle oinstall 4096 Dec  8 07:24 ae
drwxr-xr-x 2 oracle oinstall 4096 Dec  8 07:24 af
drwxr-xr-x 2 oracle oinstall 4096 Dec  8 07:24 ag
drwxr-xr-x 2 oracle oinstall 4096 Dec  8 07:24 ah
drwxr-xr-x 2 oracle oinstall 4096 Dec  8 07:24 ai
drwxr-xr-x 2 oracle oinstall 4096 Dec  8 07:24 aj

./capfiles/inst1/aa:
total 316
-rw-r--r-- 1 oracle oinstall   1762 Dec  8 07:25 wcr_c6cdah0000001.rec
-rw-r--r-- 1 oracle oinstall  16478 Dec  8 07:28 wcr_c6cf1h0000002.rec
-rw-r--r-- 1 oracle oinstall   1772 Dec  8 07:29 wcr_c6cjdh0000004.rec
-rw-r--r-- 1 oracle oinstall   1535 Dec  8 07:29 wcr_c6cnah0000005.rec
-rw-r--r-- 1 oracle oinstall   1821 Dec  8 07:41 wcr_c6cpfh0000007.rec
-rw-r--r-- 1 oracle oinstall   1815 Dec  8 07:33 wcr_c6cq6h000000a.rec
-rw-r--r-- 1 oracle oinstall   1535 Dec  8 07:34 wcr_c6cxmh000000h.rec
-rw-r--r-- 1 oracle oinstall   1427 Dec  8 07:41 wcr_c6cxvh000000j.rec
-rw-r--r-- 1 oracle oinstall   1425 Dec  8 07:41 wcr_c6czph000000k.rec
-rw-r--r-- 1 oracle oinstall   2398 Dec  8 07:49 wcr_c6dqfh000000q.rec
-rw-r--r-- 1 oracle oinstall 259321 Dec  8 08:35 wcr_c6du7h000000r.rec
-rw-r--r-- 1 oracle oinstall      0 Dec  8 07:55 wcr_c6f6yh000000t.rec
-rw-r--r-- 1 oracle oinstall      0 Dec  8 08:28 wcr_c6h3qh0000013.rec

./capfiles/inst1/ab:
total 0

./capfiles/inst1/ac:
total 0

./capfiles/inst1/ad:
total 0

./capfiles/inst1/ae:
total 0

./capfiles/inst1/af:
total 0

./capfiles/inst1/ag:
total 0

./capfiles/inst1/ah:
total 0

./capfiles/inst1/ai:
total 0

./capfiles/inst1/aj:
total 0

[oracle@mlab2 dbcapture]$ cd ./capfiles/inst1/aa
[oracle@mlab2 aa]$ ls -l
total 316
-rw-r--r-- 1 oracle oinstall   1762 Dec  8 07:25 wcr_c6cdah0000001.rec
-rw-r--r-- 1 oracle oinstall  16478 Dec  8 07:28 wcr_c6cf1h0000002.rec
-rw-r--r-- 1 oracle oinstall   1772 Dec  8 07:29 wcr_c6cjdh0000004.rec
-rw-r--r-- 1 oracle oinstall   1535 Dec  8 07:29 wcr_c6cnah0000005.rec
-rw-r--r-- 1 oracle oinstall   1821 Dec  8 07:41 wcr_c6cpfh0000007.rec
-rw-r--r-- 1 oracle oinstall   1815 Dec  8 07:33 wcr_c6cq6h000000a.rec
-rw-r--r-- 1 oracle oinstall   1535 Dec  8 07:34 wcr_c6cxmh000000h.rec
-rw-r--r-- 1 oracle oinstall   1427 Dec  8 07:41 wcr_c6cxvh000000j.rec
-rw-r--r-- 1 oracle oinstall   1425 Dec  8 07:41 wcr_c6czph000000k.rec
-rw-r--r-- 1 oracle oinstall   2398 Dec  8 07:49 wcr_c6dqfh000000q.rec
-rw-r--r-- 1 oracle oinstall 259321 Dec  8 08:35 wcr_c6du7h000000r.rec
-rw-r--r-- 1 oracle oinstall      0 Dec  8 07:55 wcr_c6f6yh000000t.rec
-rw-r--r-- 1 oracle oinstall      0 Dec  8 08:28 wcr_c6h3qh0000013.rec
[oracle@mlab2 aa]$ ls -l |wc -l
14

以上负载目录共14个文件

3. 我们LOGON一个新的Server Process

[oracle@mlab2 ~]$ sqlplus  / as sysdba

SQL*Plus: Release 11.2.0.3.0 Production on Sat Dec 8 08:37:40 2012

Copyright (c) 1982, 2011, Oracle.  All rights reserved.

Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, Automatic Storage Management, OLAP, Data Mining
and Real Application Testing options

会多出一个wcr文件

[oracle@mlab2 aa]$ ls -ltr
total 316
-rw-r--r-- 1 oracle oinstall   1762 Dec  8 07:25 wcr_c6cdah0000001.rec
-rw-r--r-- 1 oracle oinstall  16478 Dec  8 07:28 wcr_c6cf1h0000002.rec
-rw-r--r-- 1 oracle oinstall   1772 Dec  8 07:29 wcr_c6cjdh0000004.rec
-rw-r--r-- 1 oracle oinstall   1535 Dec  8 07:29 wcr_c6cnah0000005.rec
-rw-r--r-- 1 oracle oinstall   1815 Dec  8 07:33 wcr_c6cq6h000000a.rec
-rw-r--r-- 1 oracle oinstall   1535 Dec  8 07:34 wcr_c6cxmh000000h.rec
-rw-r--r-- 1 oracle oinstall   1425 Dec  8 07:41 wcr_c6czph000000k.rec
-rw-r--r-- 1 oracle oinstall   1427 Dec  8 07:41 wcr_c6cxvh000000j.rec
-rw-r--r-- 1 oracle oinstall   1821 Dec  8 07:41 wcr_c6cpfh0000007.rec
-rw-r--r-- 1 oracle oinstall   2398 Dec  8 07:49 wcr_c6dqfh000000q.rec
-rw-r--r-- 1 oracle oinstall      0 Dec  8 07:55 wcr_c6f6yh000000t.rec
-rw-r--r-- 1 oracle oinstall      0 Dec  8 08:28 wcr_c6h3qh0000013.rec
-rw-r--r-- 1 oracle oinstall 259321 Dec  8 08:35 wcr_c6du7h000000r.rec
-rw-r--r-- 1 oracle oinstall      0 Dec  8 08:37 wcr_c6hp4h0000018.rec

但是新出现的wcr_c6hp4h0000018.rec 是空的

SQL> select spid from v$process where addr = ( select paddr from v$session where sid=(select distinct sid from v$mystat));

SPID
------------------------
14293

该新的服务进程的操作系统进程号为14293, 可以看到该进程的打开文件描述符,其中就包含了wcr_c6hp4h0000018.rec

[oracle@mlab2 ~]$ ls -l /proc/14293/fd
total 0
lr-x------ 1 oracle oinstall 64 Dec  8 08:39 0 -> /dev/null
l-wx------ 1 oracle oinstall 64 Dec  8 08:39 1 -> /dev/null
lrwx------ 1 oracle oinstall 64 Dec  8 08:39 10 -> /u01/app/oracle/product/11201/db_1/rdbms/audit/CRMV_ora_14293_1.aud
l-wx------ 1 oracle oinstall 64 Dec  8 08:39 11 -> /u01/app/oracle/diag/rdbms/crmv/CRMV/trace/CRMV_ora_14293.trc
l-wx------ 1 oracle oinstall 64 Dec  8 08:39 12 -> pipe:[34585895]
l-wx------ 1 oracle oinstall 64 Dec  8 08:39 13 -> /u01/app/oracle/diag/rdbms/crmv/CRMV/trace/CRMV_ora_14293.trm
l-wx------ 1 oracle oinstall 64 Dec  8 08:39 2 -> /dev/null
lr-x------ 1 oracle oinstall 64 Dec  8 08:39 3 -> /dev/null
lr-x------ 1 oracle oinstall 64 Dec  8 08:39 4 -> /dev/null
lr-x------ 1 oracle oinstall 64 Dec  8 08:39 5 -> /u01/app/oracle/product/11201/db_1/rdbms/mesg/oraus.msb
lr-x------ 1 oracle oinstall 64 Dec  8 08:39 6 -> /proc/14293/fd
lr-x------ 1 oracle oinstall 64 Dec  8 08:39 7 -> /dev/zero
lrwx------ 1 oracle oinstall 64 Dec  8 08:39 8 -> /home/oracle/dbcapture/capfiles/inst1/aa/wcr_c6hp4h0000018.rec
lr-x------ 1 oracle oinstall 64 Dec  8 08:39 9 -> pipe:[34585894]

也可以通过lsof查到

[root@mlab2 ~]# lsof|grep wcr_c6hp4h0000018.rec
oracle    14293    oracle    8u      REG                8,1          0   17629644 /home/oracle/dbcapture/capfiles/inst1/aa/wcr_c6hp4h0000018.rec

可以得出一个结论,一个Server Process对应一个WCR REC文件,当Server Process LOGON时生成该文件

3.执行少量SQL语句:

SQL> select 1 from dual;

         1
----------
         1

SQL> /

         1
----------
         1

[oracle@mlab2 aa]$ strings wcr_c6hp4h0000018.rec

==》执行少量SQL无任何时, 不写出任何数据

执行某个超长SQL语句后,可以在下面看到其文件内容包含该WCR文件的版本号,LOGON的登录信息和所执行过的SQL语句历史,但是不包含执行计划

[oracle@mlab2 aa]$ strings wcr_c6hp4h0000018.rec
11.2.0.3.0
*File header info. (Shadow process='14293')
 D0576B5D710A34F4E043B201A8C0ECFE
SYS;
NLS_LANGUAGE?
AMERICAN>
NLS_TERRITORY?
AMERICA>
NLS_CURRENCY?
NLS_ISO_CURRENCY?
AMERICA>
NLS_NUMERIC_CHARACTERS?
NLS_CALENDAR?   GREGORIAN>
NLS_DATE_FORMAT?        DD-MON-RR>
NLS_DATE_LANGUAGE?
AMERICAN>
NLS_CHARACTERSET?
AL32UTF8>
NLS_SORT?
BINARY>
NLS_TIME_FORMAT?
HH.MI.SSXFF AM>
NLS_TIMESTAMP_FORMAT?
DD-MON-RR HH.MI.SSXFF AM>
NLS_TIME_TZ_FORMAT?
HH.MI.SSXFF AM TZR>
NLS_TIMESTAMP_TZ_FORMAT?
DD-MON-RR HH.MI.SSXFF AM TZR>
NLS_DUAL_CURRENCY?
NLS_SPECIAL_CHARS?
NLS_NCHAR_CHARACTERSET?
UTF8>
NLS_COMP?
BINARY>
NLS_LENGTH_SEMANTICS?
BYTE>
NLS_NCHAR_CONV_EXCP?
FALSE
(DESCRIPTION=(ADDRESS=(PROTOCOL=beq)(PROGRAM=/u01/app/oracle/product/11201/db_1/bin/oracle)(ARGV0=oracleCRMV)(ARGS='(DESCRIPTION=(LOCAL=YES)(ADDRESS=(PROTOCOL=beq)))')(DETACH=NO))(CONNECT_DATA=(CID=(PROGRAM=sqlplus)(HOST=mlab2.oracle.com)(USER=oracle))))
,T$sqlplus@mlab2.oracle.com (TNS V1-V3)U
        tselect spid from v$process where addr = ( select paddr from v$session where sid=(select distinct sid from v$mystat))
        `       _
select 1 from dual
select 1 from dual

执行某些错误语句时甚至会记录其错误信息
[oracle@mlab2 aa]$ strings wcr_c6hp4h0000018.rec

9`9_^B
create table vva(t1 int)
`:_i
:`:_iB
`;_^
;`;_^B
create table vva(t1 int)
`_i
>`>_iB
FusC
`?_^
?`?_^B
FvWC
        _begin
for i in 1..50000 loop
execute immediate 'select 1 from dual where 2='||i;
end loop;
end;

若SERVER PROCESS LOGOFF 则会有如下信息

C`E_    B
k^2C

以上说明Server Process并不胡在parse或execution阶段写WCR文件,而是将这些数据存放在PGA中,达到一定大小阀值后才写出,所以其性能影响小,但是如果在WCR数据写出前就意外终止则,这些数据将丢失。

4. 窥视进程信息

SQL> oradebug setmypid
Statement processed.
SQL> oradebug dump processstate 10;
Statement processed.
SQL> oradebug tracefile_name
/u01/app/oracle/diag/rdbms/crmv/CRMV/trace/CRMV_ora_14293.trc

从processstate 文件中可以发现历史等待信息包括 WCR: capture file IO write,这是Server process在写WCR 文件

     3: waited for 'SQL*Net message to client'
        driver id=0x62657100, #bytes=0x1, =0x0
        wait_id=139 seq_num=140 snap_id=1
        wait times: snap=0.000007 sec, exc=0.000007 sec, total=0.000007 sec
        wait times: max=infinite
        wait counts: calls=0 os=0
        occurred after 0.934091 sec of elapsed time
     4: waited for 'latch: shared pool'
        address=0x60106b20, number=0x133, tries=0x0
        wait_id=138 seq_num=139 snap_id=1
        wait times: snap=0.000066 sec, exc=0.000066 sec, total=0.000066 sec
        wait times: max=infinite
        wait counts: calls=0 os=0
        occurred after 1.180690 sec of elapsed time
     5: waited for 'WCR: capture file IO write'
        =0x0, =0x0, =0x0
        wait_id=137 seq_num=138 snap_id=1
        wait times: snap=0.000189 sec, exc=0.000189 sec, total=0.000189 sec
        wait times: max=infinite
        wait counts: calls=0 os=0
        occurred after 3.122783 sec of elapsed time
     6: waited for 'WCR: capture file IO write'
        =0x0, =0x0, =0x0
        wait_id=136 seq_num=137 snap_id=1
        wait times: snap=0.000191 sec, exc=0.000191 sec, total=0.000191 sec
        wait times: max=infinite
        wait counts: calls=0 os=0
        occurred after 3.053132 sec of elapsed time
     7: waited for 'WCR: capture file IO write'

5.窥视PGA内存信息

SQL> oradebug dump heapdump 536870917;
Statement processed.

grep WCR /u01/app/oracle/diag/rdbms/crmv/CRMV/trace/CRMV_ora_14293.trc

  Chunk     7fb1b606bfc0 sz=    65600    freeable  "WCR Capture PG "  ds=0x7fb1b6115f90
  Chunk     7fb1b6111e18 sz=     4224    freeable  "WCR Capture PG "  ds=0x7fb1b6115f90
  Chunk     7fb1b6112e98 sz=     4184    freeable  "WCR Capture PG "  ds=0x7fb1b6115f90
  Chunk     7fb1b6113ef0 sz=     4224    freeable  "WCR Capture PG "  ds=0x7fb1b6115f90
  Chunk     7fb1b6114f70 sz=     4104    recreate  "WCR Capture PG "  latch=(nil)
  Chunk     7fb1b6115f78 sz=      160    freeable  "WCR Capture PGA"
  Chunk     7fb1b6116018 sz=     3248    freeable  "WCR Capture PGA"
  Subheap ds=0x7fb1b6115f90  heap name=  WCR Capture PG  size=           82336
HEAP DUMP heap name="WCR Capture PG"  desc=0x7fb1b6115f90
FIVE LARGEST SUB HEAPS for heap name="WCR Capture PG"   desc=0x7fb1b6115f9

PGA中存在WCR Capture PG 和WCR Capture PGA的freeable or recreate内存chunk,这些内存可以从Server Process返回给OS

  Chunk     7fb1b606bfc0 sz=    65600    freeable  "WCR Capture PG "  ds=0x7fb1b6115f90

sz=    65600=》 64k 这可能就是文档描述的64k内存,但是实际可以看到使用总量超过了64k

WCR是否可能引起内存泄露呢?:)!

6.隐藏参数

控制WCR CAPTURE的参数至少有以下2个

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 in ('_capture_buffer_size','_wcr_control');

NAME                 VALUE                DESCRIB
-------------------- -------------------- ------------------------------------------------------------
_wcr_control         0                    Oracle internal test WCR parameter used ONLY for testing!
_capture_buffer_size 65536                To set the size of the PGA I/O recording buffers

其中_capture_buffer_size 控制PGA中WCR BUFFER的SIZE,默认为64k
_wcr_control 控制WCR的内部行为,具体尚不清晰

7.结束capture 

SQL> execute dbms_workload_capture.finish_capture;

PL/SQL procedure successfully completed.

DBMS_WORKLOAD_CAPTURE.START_CAPTURE(): Starting database capture at 12/08/2012 07:24:54
DBMS_WORKLOAD_CAPTURE.FINISH_CAPTURE(): Stopped database capture (not all sessions  could flush their capture buffers) at 12/08/2012 10:29:49

 

 

综上所述,我们得出结论:

1. 负责写WCR WORKLOAD CAPTURE文件的不是什么后台进程,而是Server Process服务进程(前台进程)
2. 每一个server process对应一个WCR文件
3. Server Proess会写出LOGON、LOGOFF、SQL执行以及错误信息到WCR文件中
4. Server Process的写不是立即模式Immediate mode,而是将数据存放在PGA的(WCR Capture) subheap中,当数据达到一定大小或一定时间(timeout才写出一次)
5. 再次强调, Server Process的写不是立即模式Immediate mode,即capture不发生在parse或者execution阶段(很多人认为capture发生在parse时这种说法可以证明为不正确,parse并不受到capture的影响),而仅仅是将LOGON、SQL历史(不包括执行计划)放在PGA的WCR Capture内存子堆中,条件触发才写出,所以其性能损耗小,按照tpcc的测试结论为4.5%
6. 隐藏参数_capture_buffer_size 控制PGA中WCR BUFFER的SIZE,默认为64k
7. WCR Capture文件虽然是binrary 2进制文件,但并未加密,所以第三方软件理论上也可以读取该WCR capture file
8. WCR: capture file IO write等待事件是Server Process在写WCR文件
9. 执行dbms_workload_capture.finish_capture;会让现有Server Process FLUSH WCR buffer,但是就ALERT.LOG的自解释信息看”Stopped database capture (not all sessions  could flush their capture buffers)”,这种flush不是必然能回收内存的 

 

沪ICP备14014813号-2

沪公网安备 31010802001379号