Oracle里未成功commit的数据我们也可能能看到

我们大家都知道的一个常识是——Oracle里未commit的数据除了当前session之外,其他session是看不到的。

我这里演示了一个有趣的例子,在这个例子里我们可以看到,Oracle里未成功commit的数据我们也可能能看到。

我同时启4session

 

先在session 1里创建一个表t1,插入一条数据但不commit

Session 1:

Connected to Oracle Database 11g Enterprise Edition Release 11.2.0.1.0

Connected as scott

SQL> create table t1(id number, name varchar2(10));

 

Table created

 

SQL> insert into t1 values(1,’CUIHUA’);

 

1 row inserted

 

SQL> select * from t1;

 

        ID NAME

———- ———-

         1 CUIHUA

 

此时跳到session 2,因session 1里刚插入的那条数据还未commit,所以这个时候session 2是看不到这条数据的:

Session 2:

SQL> select * from t1;

 

        ID NAME

———- ———-

 

接着我们跳到session 3,把lgwr进程suspend住:

Session 3:

SQL> select spid from v$process where pname=’LGWR’;

 

SPID

————————

2316

 

SQL> oradebug setospid 2316

Oracle pid: 11, Windows thread id: 2316, image: ORACLE.EXE (LGWR)

SQL> oradebug suspend

已处理的语句

 

现在我们回到session 1,执行commit命令,因为lgwr进程已经被我们suspend住了,所以当前session 1里要执行的commit操作一定会被hang住:

Session1

SQL> commit;

……这里hang住了

 

Oraclecommit操作的流程是这样的:

1Oracle先去改这个transaction所对应的undo segment headerslot的状态;

2、改完状态后再flush log buffer

现在我们把lgwr hold住了,所以上述步骤2 Oracle是没法做了,但步骤1还是可以做的。而只要步骤1做完了,其他的session就能看到这个transaction所做的改变了(通过ITL中记录的transaction idcheck相应的undo segment headerslot的状态),也就是说对于其他session而言,这个transaction已经commit了,虽然这个transaction其实并没有成功commit

 

好了,我们现在回到session 2,看一下我们现在能否看到刚才insert的那条记录:

Session 2

SQL> select * from t1;

 

        ID NAME

———- ———-

         1 CUIHUA

 

从结果里可以看到,刚才看不到的那条记录现在我们已经能看到了,即session 1对于session2而言已经commit了,虽然session1commit操作其实并没有成功的做完。

 

现在我们跳到session 4,执行shutdown abort

Session 4

SQL> shutdown abort

ORACLE 例程已经关闭。

 

然后我们再在session 4中执行startupstartup后我们从alert log里可以很明显的看到Oracle做了instance recovery

Sat Jan 28 19:14:03 2012

alter database mount exclusive

Successful mount of redo thread 1, with mount id 4288810811

Database mounted in Exclusive Mode

Lost write protection disabled

Completed: alter database mount exclusive

alter database open

Beginning crash recovery of 1 threads

 parallel recovery started with 2 processes

Started redo scan

Completed redo scan

 read 918 KB redo, 126 data blocks need recovery

Started redo application at

 Thread 1: logseq 44, block 77312

Recovery of Online Redo Log: Thread 1 Group 2 Seq 44 Reading mem 0

  Mem# 0: C:\APP\CUIHUA\ORADATA\CUIHUA112\REDO02.LOG

Completed redo application of 0.52MB

Completed crash recovery at

 Thread 1: logseq 44, block 79149, scn 1652578

 126 data blocks read, 95 data blocks written, 918 redo k-bytes read

 

等待上述库成功startup后,我们再次回到session 1,看一下刚才我们已经commit的那条数据是否还在:

Session 1

SQL> select * from t1;

 

        ID NAME

———- ———-

 

从结果里可以看到,刚才我们insert且执行了commit操作的那条记录现在已经丢失了。

 


MSSM的段头中CCNT的含义(续)

其实这篇文章的结论并不重要,最关键的是当你碰到一个在googlebaiduMOS甚至Oracle内部的webiv上都查不到相关内容的时候,你处理的思路是什么。

经过简单的测试­,我们猜测——MSSM的段头中CCNT的含义就是Change CouNT的缩写,它的含义是OracleMSSM中段头的free list的修改次数,一次修改通常会包括move hwm+link block+unlink block这一组动作。

 

我们现在来证明一下上述结论:

SQL> create table t1 as select * from dba_objects where rownum<100;

 

Table created

 

SQL> insert into t1 select * from dba_objects where rownum<1000;

 

999 rows inserted

 

SQL> commit;

 

Commit complete

 

SQL> select header_file,header_block from dba_segments where segment_name=’T1′;

 

HEADER_FILE HEADER_BLOCK

———– ————

1                           51065

 

t1之前的段头内容为:

buffer tsn: 0 rdba: 0x0040c779 (1/51065)

scn: 0×0000.000ec812 seq: 0×04 flg: 0×00 tail: 0xc8121004

frmt: 0×02 chkval: 0×0000 type: 0×10=DATA SEGMENT HEADER – UNLIMITED

  Extent Control Header

  —————————————————————–

  Extent Header:: spare1: 0      spare2: 0      #extents: 10     #blocks: 79   

                  last map  0×00000000  #maps: 0      offset: 4128 

      Highwater::  0x0040c7d6  ext#: 9      blk#: 5      ext size: 8    

  #blocks in seg. hdr’s freelists: 3    

  #blocks below: 76   

  mapblk  0×00000000  offset: 9    

                   Unlocked

     Map Header:: next  0×00000000  #extents: 10   obj#: 30547  flag: 0×40000000

  Extent Map

  —————————————————————–

   0x0040c77a  length: 7    

   0x0040c781  length: 8    

   0x0040c791  length: 8    

   0x0040c799  length: 8    

   0x0040c7a1  length: 8    

   0x0040c7a9  length: 8    

   0x0040c7b9  length: 8    

   0x0040c7c1  length: 8    

   0x0040c7c9  length: 8    

   0x0040c7d1  length: 8    

 

  nfl = 1, nfb = 1 typ = 1 nxf = 0 ccnt = 21

  SEG LST:: flg: USED   lhd: 0x0040c7d3 ltl: 0x0040c7d5

 

当我对t1执行insert大量数据的操作时,Oracle必然要去修改t1段头中的freelist

SQL> insert into t1 select * from t1 where rownum<1000;

 

999 rows inserted

 

SQL> commit;

 

Commit complete

 

执行完上述插入操作后,t1的段头中内容如下所示,从结果里我们可以看到ccnt21变成了25

Start dump data blocks tsn: 0 file#: 1 minblk 51065 maxblk 51065

buffer tsn: 0 rdba: 0x0040c779 (1/51065)

scn: 0×0000.000ed214 seq: 0×02 flg: 0×00 tail: 0xd2141002

frmt: 0×02 chkval: 0×0000 type: 0×10=DATA SEGMENT HEADER – UNLIMITED

  Extent Control Header

  —————————————————————–

  Extent Header:: spare1: 0      spare2: 0      #extents: 12     #blocks: 95   

                  last map  0×00000000  #maps: 0      offset: 4128 

      Highwater::  0x0040c7e6  ext#: 11     blk#: 5      ext size: 8    

  #blocks in seg. hdr’s freelists: 5    

  #blocks below: 92   

  mapblk  0×00000000  offset: 11   

                   Unlocked

     Map Header:: next  0×00000000  #extents: 12   obj#: 30547  flag: 0×40000000

  Extent Map

  —————————————————————–

   0x0040c77a  length: 7    

   0x0040c781  length: 8    

   0x0040c791  length: 8    

   0x0040c799  length: 8    

   0x0040c7a1  length: 8    

   0x0040c7a9  length: 8    

   0x0040c7b9  length: 8    

   0x0040c7c1  length: 8    

   0x0040c7c9  length: 8    

   0x0040c7d1  length: 8    

   0x0040c7d9  length: 8    

   0x0040c7e1  length: 8    

 

  nfl = 1, nfb = 1 typ = 1 nxf = 0 ccnt = 25

  SEG LST:: flg: USED   lhd: 0x0040c7e1 ltl: 0x0040c7e5

 

现在的关键问题是,这个ccnt的修改之后的值25是如何来的?只有在我们清楚了这个25的来历,我们才算真正的对上述问题有了一个初步的了解。

 

这时候我们的思路就是dump一下redo,然后看看redo的内容我们就什么都清楚了,dump之后的redo中清晰的显示出了ccnt的值的变化过程:

过程1

REDO RECORD – Thread:1 RBA: 0x00000a.00000041.004c LEN: 0×0070 VLD: 0×01

SCN: 0×0000.000ed210 SUBSCN:  1 05/16/2011 17:07:29

CHANGE #1 TYP:0 CLS: 4 AFN:1 DBA:0x0040c779 SCN:0×0000.000ec812 SEQ:  4 OP:13.7

KTSFRGRP (fgb/shdr modify freelist) redo:

 Opcode: LUPD_UNLBLK (unlink block)

Slot no: 0, Count: 1

Flag: = 1 xid or slot0 ccnt:  0×0000.000.00000015 Head:  0x0040c7d4 Tail:  0x0040c7d5

CHANGE #2 TYP:0 CLS: 1 AFN:1 DBA:0x0040c7d3 SCN:0×0000.000ed210 SEQ:  2 OP:13.6

KTSFRBLNK (block link modify) redo:  Opcode: LCLR (lock clear)

Next dba: 0×00000000 itli: 0

 

过程2

REDO RECORD – Thread:1 RBA: 0x00000a.00000051.0098 LEN: 0×0070 VLD: 0×01

SCN: 0×0000.000ed210 SUBSCN:  1 05/16/2011 17:07:29

CHANGE #1 TYP:0 CLS: 4 AFN:1 DBA:0x0040c779 SCN:0×0000.000ed210 SEQ:  1 OP:13.7

KTSFRGRP (fgb/shdr modify freelist) redo:

 Opcode: LUPD_UNLBLK (unlink block)

Slot no: 0, Count: 1

Flag: = 1 xid or slot0 ccnt:  0×0000.000.00000015 Head:  0x0040c7d5 Tail:  0x0040c7d5

CHANGE #2 TYP:0 CLS: 1 AFN:1 DBA:0x0040c7d4 SCN:0×0000.000ed210 SEQ:  2 OP:13.6

KTSFRBLNK (block link modify) redo:  Opcode: LCLR (lock clear)

Next dba: 0×00000000 itli: 0

 

过程3

REDO RECORD – Thread:1 RBA: 0x00000a.00000061.00ec LEN: 0×0070 VLD: 0×01

SCN: 0×0000.000ed210 SUBSCN:  1 05/16/2011 17:07:29

CHANGE #1 TYP:0 CLS: 4 AFN:1 DBA:0x0040c779 SCN:0×0000.000ed210 SEQ:  2 OP:13.7

KTSFRGRP (fgb/shdr modify freelist) redo:

 Opcode: LUPD_UNLBLK (unlink block)

Slot no: 0, Count: 1

Flag: = 1 xid or slot0 ccnt:  0×0000.000.00000015 Head:  0×00000000 Tail:  0×00000000

CHANGE #2 TYP:0 CLS: 1 AFN:1 DBA:0x0040c7d5 SCN:0×0000.000ed210 SEQ:  2 OP:13.6

KTSFRBLNK (block link modify) redo:  Opcode: LCLR (lock clear)

Next dba: 0×00000000 itli: 0

 

过程4

REDO RECORD – Thread:1 RBA: 0x00000a.00000062.0098 LEN: 0x007c VLD: 0×01

SCN: 0×0000.000ed210 SUBSCN:  1 05/16/2011 17:07:29

CHANGE #1 TYP:0 CLS: 1 AFN:1 DBA:0x0040c7d8 SCN:0×0000.000ed210 SEQ:  3 OP:13.6

KTSFRBLNK (block link modify) redo:  Opcode: LWRT (lock write)

Next dba: 0×00000000 itli: 0

CHANGE #2 TYP:0 CLS: 4 AFN:1 DBA:0x0040c779 SCN:0×0000.000ed210 SEQ:  3 OP:13.7

KTSFRGRP (fgb/shdr modify freelist) redo:

 Opcode: HWMMV (move hwm)

NBK: 3

 Opcode: LUPD_LLIST (link a list)

Slot no: 0, Count: 3

Flag: = 1 xid or slot0 ccnt:  0×0000.000.00000016 Head:  0x0040c7d6 Tail:  0x0040c7d8

 

过程5

REDO RECORD – Thread:1 RBA: 0x00000a.00000072.00e8 LEN: 0×0070 VLD: 0×01

SCN: 0×0000.000ed210 SUBSCN:  1 05/16/2011 17:07:29

CHANGE #1 TYP:0 CLS: 4 AFN:1 DBA:0x0040c779 SCN:0×0000.000ed210 SEQ:  4 OP:13.7

KTSFRGRP (fgb/shdr modify freelist) redo:

 Opcode: LUPD_UNLBLK (unlink block)

Slot no: 0, Count: 1

Flag: = 1 xid or slot0 ccnt:  0×0000.000.00000016 Head:  0x0040c7d7 Tail:  0x0040c7d8

CHANGE #2 TYP:0 CLS: 1 AFN:1 DBA:0x0040c7d6 SCN:0×0000.000ed210 SEQ:  5 OP:13.6

KTSFRBLNK (block link modify) redo:  Opcode: LCLR (lock clear)

Next dba: 0×00000000 itli: 0

 

过程6

REDO RECORD – Thread:1 RBA: 0x00000a.00000082.003c LEN: 0×0070 VLD: 0×01

SCN: 0×0000.000ed210 SUBSCN:  1 05/16/2011 17:07:29

CHANGE #1 TYP:0 CLS: 4 AFN:1 DBA:0x0040c779 SCN:0×0000.000ed210 SEQ:  5 OP:13.7

KTSFRGRP (fgb/shdr modify freelist) redo:

 Opcode: LUPD_UNLBLK (unlink block)

Slot no: 0, Count: 1

Flag: = 1 xid or slot0 ccnt:  0×0000.000.00000016 Head:  0x0040c7d8 Tail:  0x0040c7d8

CHANGE #2 TYP:0 CLS: 1 AFN:1 DBA:0x0040c7d7 SCN:0×0000.000ed210 SEQ:  4 OP:13.6

KTSFRBLNK (block link modify) redo:  Opcode: LCLR (lock clear)

Next dba: 0×00000000 itli: 0

 

过程7

REDO RECORD – Thread:1 RBA: 0x00000a.00000092.00a8 LEN: 0×0070 VLD: 0×01

SCN: 0×0000.000ed210 SUBSCN:  1 05/16/2011 17:07:29

CHANGE #1 TYP:0 CLS: 4 AFN:1 DBA:0x0040c779 SCN:0×0000.000ed210 SEQ:  6 OP:13.7

KTSFRGRP (fgb/shdr modify freelist) redo:

 Opcode: LUPD_UNLBLK (unlink block)

Slot no: 0, Count: 1

Flag: = 1 xid or slot0 ccnt:  0×0000.000.00000016 Head:  0×00000000 Tail:  0×00000000

CHANGE #2 TYP:0 CLS: 1 AFN:1 DBA:0x0040c7d8 SCN:0×0000.000ed210 SEQ:  6 OP:13.6

KTSFRBLNK (block link modify) redo:  Opcode: LCLR (lock clear)

Next dba: 0×00000000 itli: 0

 

过程8

REDO RECORD – Thread:1 RBA: 0x00000a.00000097.0030 LEN: 0x007c VLD: 0×01

SCN: 0×0000.000ed212 SUBSCN:  1 05/16/2011 17:07:29

CHANGE #1 TYP:0 CLS: 1 AFN:1 DBA:0x0040c7dd SCN:0×0000.000ed212 SEQ:  3 OP:13.6

KTSFRBLNK (block link modify) redo:  Opcode: LWRT (lock write)

Next dba: 0×00000000 itli: 0

CHANGE #2 TYP:0 CLS: 4 AFN:1 DBA:0x0040c779 SCN:0×0000.000ed212 SEQ:  1 OP:13.7

KTSFRGRP (fgb/shdr modify freelist) redo:

 Opcode: HWMMV (move hwm)

NBK: 5

 Opcode: LUPD_LLIST (link a list)

Slot no: 0, Count: 5

Flag: = 1 xid or slot0 ccnt:  0×0000.000.00000017 Head:  0x0040c7d9 Tail:  0x0040c7dd

 

……省略显示其他类似过程

 

过程18

REDO RECORD – Thread:1 RBA: 0x00000a.0000011e.0010 LEN: 0x007c VLD: 0×01

SCN: 0×0000.000ed214 SUBSCN:  1 05/16/2011 17:07:29

CHANGE #1 TYP:0 CLS: 1 AFN:1 DBA:0x0040c7e5 SCN:0×0000.000ed214 SEQ:  3 OP:13.6

KTSFRBLNK (block link modify) redo:  Opcode: LWRT (lock write)

Next dba: 0×00000000 itli: 0

CHANGE #2 TYP:0 CLS: 4 AFN:1 DBA:0x0040c779 SCN:0×0000.000ed214 SEQ:  1 OP:13.7

KTSFRGRP (fgb/shdr modify freelist) redo:

 Opcode: HWMMV (move hwm)

NBK: 5

 Opcode: LUPD_LLIST (link a list)

Slot no: 0, Count: 5

Flag: = 1 xid or slot0 ccnt:  0×0000.000.00000019 Head:  0x0040c7e1 Tail:  0x0040c7e5

 

从上述过程1一直到过程18,非常完整的显示了Oracle是如何将ccnt的值从210×15)递增到250×19)的。

现在我们来尝试解释一下上述过程的含义:

上述过程123为一组,清晰的表示了Oracleinsert大量数据后是如何开始修改MSSM的段头的freelist的,从t1insert之前的段头的dump内容中我们可以看到如下内容:

nfl = 1, nfb = 1 typ = 1 nxf = 0 ccnt = 21

SEG LST:: flg: USED   lhd: 0x0040c7d3 ltl: 0x0040c7d5

即表明t1main freelist已经存在且header指向0x0040c7d3tail指向ltl: 0x0040c7d5

那么现在当Oracle插入大量数据的时候,因为freelist上已经存在3block(分别是0x0040c7d30x0040c7d40x0040c7d5),所以就直接拿来用好了,也就是过程1、过程2、过程3所对应的unlink block操作。

等到过程1、过程2、过程3都执行完了,现在t1main freelist上已经不存在可用的block,所以Oracle这时候要扩展高水位线,给t1main freelist先分配一些可用的free block,也就是过程4所表明的含义。在过程4里,Oracle额外分配了3block(分别是0x0040c7d60x0040c7d7 0x0040c7d8),把高水位线扩展到了0x0040c7d9且对这三个block执行了block link操作,以便让其处于main freelist中。接下来过程567做的事情和过程123类似,都是为了用freelist上存在的3block而执行的unlink block操作。所以过程4567其实是另外一组操作,注意看,过程4567所对应的ccnt已经递增了1

 

后续的分析与上面类似,最后的结果就是ccnt递增到0×19就终止了,即这次insert操作做完了后,t1main free list中的free block变成了5个(从0x0040c7e10x0040c7e5)。

 

所以,从上面的分析我们可以得出结论:ccnt其实记录的是在MSSMOracle对段头的freelist的一组修改操作所对应的sequence number,相邻的组与组之间的ccnt会递增1

一组修改操作的含义是如下三种情况之一:

1、  单纯的unlink block(例如过程123);

2、  link block unlink block (例如过程4567

3、  单纯的link block (例如过程18

 

简单总结一下:MSSM的段头中CCNT我猜是Change CouNT的缩写,它的含义是OracleMSSM中段头的free list的修改次数,一次修改通常会包括move hwm+link block+unlink block这一组动作。