zoukankan      html  css  js  c++  java
  • HWM高水标记

    本文讨论的是oracle中关于tableHWM的内容,主要包括这样几个内容:

    1.    什么是HWM

    2.    HWM是如何移动的。

    3.    HWM对于性能的影响

    4.    何时应该降低以及如何降低HWM

    5.    其他一些影响HWM的操作

    除了特殊注明,本章内容所有的实验都基于:win2000,oracle9201,bolcksize 8K的实验环境。

    一、什么是HWM

    HWMhigh water mark,高水标记,这个概念在segment的存储内容中是比较重要的.简单来说,HWM就是一个segment中已使用和未使用的block的分界线.

    oracleconcept中对于HWM的说明是这样的:在一个segment中,HWM是使用和未使用空间的分界线。当请求新的空闲块,并且现有空闲列表中的块不能满足要求时,HWM指向的块将被标记为已使用,然后HWM将移动指向下一个未使用过的块。

    我们知道,oracle,存储数据的最小单元是block,对于一个segment(tableindex),都是由很多的block组成的,这些block的状态分为已使用和未使用两种,一般来说,在HWM之下的block都是存储过数据的. 如图:

    从上面的图,我们就能很清楚的看到,一个segment中的block的分布情况。在HWM左边的block是已使用的,或者说是可以用来存储数据的。而HWM右边的block是不能用来存储数据的。当HWM左边的block空间都使用完之后,还有新的数据需要存储,怎样处理呢?这时oracle会向右移动HWM,即把右边的没有使用的block移到HWM的左边,这时HWM左边的block就增加了,那么就有新的block空间可供使用了。

    Oracle9i开始,推出了新的一种segment的空间管理方式,即ASSMauto segment space management)。这种segment在空间管理上和以前的FLMfreelist management)是不一样的。这里我们简单地介绍一下。

    FLM模式下,对于一个segmentHWM下的所有block空间的使用,是通过freelist来管理的,freelist位于segment的第一个extent中。一个block何时应该位于freelist之上,取决于PCTUSEDPCTFREE这样两个参数。基于freelist管理模式和位于segment header的情况,如果对一个segment进行高并发的频繁的DML操作,不可避免的出现header争用的情况,虽然我们可以采用增加freelistsfreelist group的方式来缓解这种状况。

    那么从oracle92开始,推出了ASSM这样一种全新的segmeng空间管理的方式(又称为Bitmap Managed Segments, freelist被位图所取代,使用位图来管理block的空间使用状况,并且这些位图块分散在segment中。ASSM管理的segment会略掉任何为PCTUSEDNEXTFREELISTS所指定的值。

    使用ASSM也有一定的局限性:

    ASSM只能位于Local Managetablespace之上;

    不能够使用ASSM创建临时的tablespace

    LOB对象不能在一个指定进行自动段空间管理的tablespace中创建。

    以上我们简单地介绍了ASSMFLM的概念和区别,接下来,我们来看看这两种segmeng空间管理模式在HWM的处理上有什么不同。

    二、初始创建的tableHWM的不同情况

     

    FLM管理的table:我们先创建名为HWMtablespace,指定非自动段空间管理,extent大小为40K。并在上面创建table TEST_HWMPCTFREE 40 PCTUSED 20

    SQL> connect dlinger/dlinger@oracle9i_dl

    连接到:

    Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production

    With the Partitioning, OLAP and Oracle Data Mining options

    JServer Release 9.2.0.1.0 - Production

    SQL> CREATE TABLESPACE HWM

    2      DATAFILE 'D:\ORACLE\ORADATA\ORACLE9I\HWM.dbf'

    3      SIZE 50M uniform size 40K;

    表空间已创建。

    SQL> select TABLESPACE_NAME,BLOCK_SIZE,EXTENT_MANAGEMENT,

     2 ALLOCATION_TYPE, SEGMENT_SPACE_MANAGEMENT

     3 from dba_tablespaces where TABLESPACE_NAME = 'HWM';

     

     

    TABLESPACE_NAME BLOCK_SIZE EXTENT_MANAGEMENT ALLOCATION_TYPE SEGMENT_SPACE_MANAGEMENT

    --------------- ---------- ----------------- --------------- ------------------------

    HWM                   8192 LOCAL             UNIFORM         MANUAL

     

     

    SQL> alter user dlinger default tablespace hwm;

    用户已更改。

    SQL> CREATE TABLE TEST_HWM (ID CHAR(2000) , NAME CHAR(2000) )

     2 STORAGE ( MINEXTENTS 2) PCTFREE 40 PCTUSED 20;

    表已创建。

    SQL>select EXTENT_ID,FILE_ID,RELATIVE_FNO,BLOCK_ID,BLOCKS

    from dba_extents

     2 where segment_name='TEST_HWM' ;

     EXTENT_ID    FILE_ID RELATIVE_FNO   BLOCK_ID     BLOCKS

    ---------- ---------- ------------ ---------- ----------

             0         11           11          9          5

             1         11           11         14          5

    SQL> alter system dump datafile 11 block 9;

    系统已更改。

    Table TEST_HWM位于datafile 11 segment headerblock9,我们dumpblock9来看看:

    *** 2004-06-09 20:31:26.000

    *** SESSION ID:(9.5) 2004-06-09 20:31:26.000

    Start dump data blocks tsn: 14 file#: 11 minblk 9 maxblk 9

    buffer tsn: 14 rdba: 0x02c00009 (11/9)

    scn: 0x0000.013e974e seq: 0x01 flg: 0x00 tail: 0x974e1001

    frmt: 0x02 chkval: 0x0000 type: 0x10=DATA SEGMENT HEADER - UNLIMITED

     Extent Control Header

     -----------------------------------------------------------------

     Extent Header:: spare1: 0      spare2: 0      #extents: 2      #blocks: 9    

                      last map 0x00000000 #maps: 0      offset: 4128 

          Highwater:: 0x02c0000a ext#: 0      blk#: 0      ext size: 4    

     #blocks in seg. hdr's freelists: 0    

     #blocks below: 0    

     mapblk  0x00000000 offset: 0    

                       Unlocked

         Map Header:: next 0x00000000 #extents: 2    obj#: 32377 flag: 0x40000000

     Extent Map

     -----------------------------------------------------------------

       0x02c0000a length: 4    

       0x02c0000e length: 5    

     

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

     SEG LST:: flg: UNUSED lhd: 0x00000000 ltl: 0x00000000

    End dump data blocks tsn: 14 file#: 11 minblk 9 maxblk 9

    dumptrace文件,我们可以获得这样的信息:

    我们可以看到Highwater:: 0x02c0000aFLMsegment下,初始创建的tableHWM是从第一个extent的第二个block开始的。为segment header 保留一个块。我们从这里也可以看出来:

    Extent Map

       -----------------------------------------------------------------

       0x02c0000a length: 4    

    说明第一个extent可用的block4

    我们这里看到的结果是在默认freelist 1的条件下得到的。在FLM下,如果对segment设置了freelist groups N,则HWM指向第 N2block,当N+2 > initextentblock数时,会返回ORA-03237的错误信息,这里tablespace HWMextent40Kblock_size 8K

    SQL> CREATE TABLE TEST_HWM2 (ID CHAR(2000) , NAME CHAR(2000) )

     2   STORAGE ( MINEXTENTS 2 freelist groups 4) PCTFREE 40 PCTUSED 20;

     CREATE TABLE TEST_HWM2 (ID NUMBER(10) , NAME CHAR(2000) )

    *

    ERROR 位于第 1 :

    ORA-03237: 在表空间 (HWM) 无法分配指定大小的初始区

    ASSM下,情况是怎样的呢?

    我们创建名为ASSMtablespace,指定自动段空间管理,extent大小为40K。并在上面创建table TEST_HWM1,注意,这里我们只指定了PCTFREE 40,因为PCTUSEDASSM下的segment中是无效的。

    SQL> CREATE TABLESPACE ASSM

    2      DATAFILE 'D:\ORACLE\ORADATA\ORACLE9I\ASSM.dbf'

    3      SIZE 50M uniform size 40K segment space management auto;

    表空间已创建。

    SQL> select TABLESPACE_NAME,BLOCK_SIZE,EXTENT_MANAGEMENT,

     2 ALLOCATION_TYPE, SEGMENT_SPACE_MANAGEMENT

     3 from dba_tablespaces where TABLESPACE_NAME = 'ASSM';

    TABLESPACE_NAME BLOCK_SIZE EXTENT_MANAGEMENT ALLOCATION_TYPE SEGMENT_SPACE_MANAGEMENT

    --------------- ---------- ----------------- --------------- ------------------------

    ASSM                  8192 LOCAL             UNIFORM         AUTO

    SQL> CREATE TABLE TEST_HWM1 (ID CHAR(2000), NAME CHAR(2000) )

     2 Tablespace ASSM

     3 STORAGE ( MINEXTENTS 2) PCTFREE 40;

    表已创建。

    SQL> select EXTENT_ID,FILE_ID,RELATIVE_FNO,BLOCK_ID,BLOCKS

     2 from dba_extents

     3 where segment_name='TEST_HWM1' ;

     EXTENT_ID    FILE_ID RELATIVE_FNO   BLOCK_ID     BLOCKS

    ---------- ---------- ------------ ---------- ----------

             0         12           12          9          5

             1         12           12         14          5

    SQL> alter system dump datafile 12 block min 9 block max 11;

    系统已更改。

    FMT下,segment的第一个block是存储segment header的,在本例中,ASSM下,oracle使用segment的至少前3block来存储segment header。这里,我们dump 911block信息。我们来看一下dump的结果和FMT下有什么不同:

    Start dump data blocks tsn: 15 file#: 12 minblk 9 maxblk 11

    buffer tsn: 15 rdba: 0x03000009 (12/9)

    scn: 0x0000.01ca6d7f seq: 0x02 flg: 0x00 tail: 0x6d7f2002

    frmt: 0x02 chkval: 0x0000 type: 0x20=FIRST LEVEL BITMAP BLOCK

    Dump of First Level Bitmap Block

     --------------------------------

       nbits : 4 nranges: 2         parent dba: 0x0300000a   poffset: 0

       unformatted: 7       total: 10        first useful block: 3

       owning instance : 1

       instance ownership changed at

       Last successful Search

       Freeness Status: nf1 0      nf2 0      nf3 0      nf4 0

       Extent Map Block Offset: 4294967295

       First free datablock : 3

       Bitmap block lock opcode 3

       Locker xid:     : 0x0004.008.0000713c

          Highwater:: 0x0300000c ext#: 0      blk#: 3      ext size: 5

     #blocks in seg. hdr's freelists: 0

     #blocks below: 0

     mapblk 0x00000000 offset: 0

     HWM Flag: HWM Set

     --------------------------------------------------------

     DBA Ranges :

     --------------------------------------------------------

       0x03000009 Length: 5      Offset: 0

       0x0300000e Length: 5      Offset: 5

       0:Metadata   1:Metadata   2:Metadata   3:unformatted

       4:unformatted   5:unformatted   6:unformatted   7:unformatted

       8:unformatted   9:unformatted

     --------------------------------------------------------

    buffer tsn: 15 rdba: 0x0300000a (12/10)

    scn: 0x0000.01ca6d7e seq: 0x02 flg: 0x00 tail: 0x6d7e2102

    frmt: 0x02 chkval: 0x0000 type: 0x21=SECOND LEVEL BITMAP BLOCK

    Dump of Second Level Bitmap Block

       number: 1       nfree: 1       ffree: 0      pdba:     0x0300000b

     opcode:0

     xid:

     L1 Ranges :

     --------------------------------------------------------

       0x03000009 Free: 5 Inst: 1

     --------------------------------------------------------

    buffer tsn: 15 rdba: 0x0300000b (12/11)

    scn: 0x0000.01ca6d80 seq: 0x01 flg: 0x00 tail: 0x6d802301

    frmt: 0x02 chkval: 0x0000 type: 0x23=PAGETABLE SEGMENT HEADER

     Extent Control Header

     -----------------------------------------------------------------

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

                      last map 0x00000000 #maps: 0      offset: 2716

          Highwater:: 0x0300000c ext#: 0      blk#: 3      ext size: 5

     #blocks in seg. hdr's freelists: 0

     #blocks below: 0

     mapblk 0x00000000 offset: 0

                       Unlocked

     --------------------------------------------------------

     Low HighWater Mark :

          Highwater:: 0x0300000c ext#: 0      blk#: 3      ext size: 5

     #blocks in seg. hdr's freelists: 0

     #blocks below: 0

     mapblk 0x00000000 offset: 0

     Level 1 BMB for High HWM block: 0x03000009

     Level 1 BMB for Low HWM block: 0x03000009

     --------------------------------------------------------

     Segment Type: 1 nl2: 1      blksz: 8192   fbsz: 0

     L2 Array start offset: 0x00001434

     First Level 3 BMB: 0x00000000

     L2 Hint for inserts: 0x0300000a

     Last Level 1 BMB: 0x03000009

     Last Level II BMB: 0x0300000a

     Last Level III BMB: 0x00000000

         Map Header:: next 0x00000000 #extents: 2    obj#: 32499 flag: 0x20000000

     Extent Map

     -----------------------------------------------------------------

       0x03000009 length: 5

       0x0300000e length: 5

     Auxillary Map

     --------------------------------------------------------

       Extent 0     : L1 dba: 0x03000009 Data dba: 0x0300000c

       Extent 1     : L1 dba: 0x03000009 Data dba: 0x0300000e

     --------------------------------------------------------

       Second Level Bitmap block DBAs

       --------------------------------------------------------

       DBA 1:   0x0300000a

    这里可以看到Highwater:: 0x0300000c HWM指向的第一个extent的第四个block,也就是说,segment head保留了3block

    为什么前面我们说oracleASSMsegment中至少用前3block来存储segment header的信息呢?我们可以创建一个extent256K tablespace来,然后在上面创建table,来看看结果:

    SQL> create tablespace assm

     2 datafile '/data1/oracle/oradata/assm01.dbf'

     3 size 10M

     4 extent management local uniform size 256K

     5 segment space management auto

     6 /

    Tablespace created.

    SQL> CREATE TABLE TEST_HWM1     (ID CHAR(2000), NAME CHAR(2000) )

     2 Tablespace ASSM

     3 STORAGE ( MINEXTENTS 2) PCTFREE 40

     4 /

    Table created.

    SQL> select EXTENT_ID,FILE_ID,RELATIVE_FNO,BLOCK_ID,BLOCKS

     2 from dba_extents

     3 where segment_name='TEST_HWM1'

     4 /

     EXTENT_ID    FILE_ID RELATIVE_FNO   BLOCK_ID     BLOCKS

    ---------- ---------- ------------ ---------- ----------

             0          7            7          9         32

             1          7            7         41         32

    SQL> alter system dump datafile 7 block min 9 block max 11;

    System altered.

    我们看其中一部分的trace文件的内容:

    Start dump data blocks tsn: 16 file#: 7 minblk 9 maxblk 11

    buffer tsn: 16 rdba: 0x01c00009 (7/9)

    scn: 0x0000.01444ea9 seq: 0x02 flg: 0x00 tail: 0x4ea92002

    frmt: 0x02 chkval: 0x0000 type: 0x20=FIRST LEVEL BITMAP BLOCK

    Dump of First Level Bitmap Block

     --------------------------------

       nbits : 4 nranges: 1         parent dba: 0x01c0000b   poffset: 0    

       unformatted: 12      total: 16        first useful block: 4     

       owning instance : 1

       instance ownership changed at

       Last successful Search

       Freeness Status: nf1 0      nf2 0      nf3 0      nf4 0     

       Extent Map Block Offset: 4294967295

       First free datablock : 4     

       Bitmap block lock opcode 0

       Locker xid:     : 0x0000.000.00000000

          Highwater:: 0x01c0000d ext#: 0      blk#: 4      ext size: 32   

     #blocks in seg. hdr's freelists: 0    

     #blocks below: 0    

     mapblk 0x00000000 offset: 0    

     HWM Flag: HWM Set

     --------------------------------------------------------

     DBA Ranges :

     --------------------------------------------------------

       0x01c00009 Length: 16     Offset: 0     

       0:Metadata   1:Metadata   2:Metadata   3:Metadata

       4:unformatted   5:unformatted   6:unformatted   7:unformatted

       8:unformatted   9:unformatted   10:unformatted   11:unformatted

       12:unformatted   13:unformatted   14:unformatted   15:unformatted

     --------------------------------------------------------

    我们发现,这里使用了前4block来存储segment header的内容。

    三、 insert数据时HWM的移动

    LMT:

    SQL> insert into test_hwm values('1','dlinger');

    已创建 1 行。

    SQL> alter system dump datafile 11 block 9;

    系统已更改。

    SQL> insert into test_hwm values('2','dlinger');

    已创建 1 行。

    SQL> alter system dump datafile 11 block 9;

    系统已更改。

    SQL> insert into test_hwm values('3','dlinger');

    已创建 1 行。

    SQL> alter system dump datafile 11 block 9;

    系统已更改。

    SQL> insert into test_hwm values('4','dlinger');

    已创建 1 行。

    SQL> alter system dump datafile 11 block 9;

    系统已更改。

    SQL> insert into test_hwm values('5','dlinger');

    已创建 1 行。

    SQL> alter system dump datafile 11 block 9;

    系统已更改。

    查看_bump_highwater_mark_count参数:

    select x.ksppinm name, y.ksppstvl value,

    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
    '\_%' escape '\' and
    x.ksppinm like
    '%bump_highwater_mark_count%'
    order by
    translate(x.ksppinm,
    ' _', ' ');

    NAME                          VALUE   

    ----------------------------- --------

    _bump_highwater_mark_count         0       

    看看dump的结果:

    *** 2004-06-14 10:46:56.000

    Start dump data blocks tsn: 14 file#: 11 minblk 9 maxblk 9

    buffer tsn: 14 rdba: 0x02c00009 (11/9)

    scn: 0x0000.015032ef seq: 0x01 flg: 0x00 tail: 0x32ef1001

    frmt: 0x02 chkval: 0x0000 type: 0x10=DATA SEGMENT HEADER - UNLIMITED

     Extent Control Header

     -----------------------------------------------------------------

     Extent Header:: spare1: 0      spare2: 0      #extents: 2      #blocks: 9    

                      last map 0x00000000 #maps: 0      offset: 4128 

          Highwater:: 0x02c0000b ext#: 0      blk#: 1      ext size: 4    

     #blocks in seg. hdr's freelists: 1    

     #blocks below: 1    

     mapblk 0x00000000 offset: 0    

                       Unlocked

         Map Header:: next 0x00000000 #extents: 2    obj#: 32387 flag: 0x40000000

     Extent Map

     -----------------------------------------------------------------

       0x02c0000a length: 4    

       0x02c0000e length: 5    

     

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

     SEG LST:: flg: USED   lhd: 0x02c0000a ltl: 0x02c0000a

    End dump data blocks tsn: 14 file#: 11 minblk 9 maxblk 9

    *** 2004-06-14 10:47:25.000

    Start dump data blocks tsn: 14 file#: 11 minblk 9 maxblk 9

    buffer tsn: 14 rdba: 0x02c00009 (11/9)

    scn: 0x0000.01503349 seq: 0x02 flg: 0x00 tail: 0x33491002

    frmt: 0x02 chkval: 0x0000 type: 0x10=DATA SEGMENT HEADER - UNLIMITED

     Extent Control Header

     -----------------------------------------------------------------

     Extent Header:: spare1: 0      spare2: 0      #extents: 2      #blocks: 9    

                      last map 0x00000000 #maps: 0      offset: 4128 

          Highwater:: 0x02c0000c ext#: 0      blk#: 2      ext size: 4    

     #blocks in seg. hdr's freelists: 1    

     #blocks below: 2    

     mapblk 0x00000000 offset: 0    

                       Unlocked

         Map Header:: next 0x00000000 #extents: 2    obj#: 32387 flag: 0x40000000

     Extent Map

     -----------------------------------------------------------------

       0x02c0000a length: 4    

       0x02c0000e length: 5    

     

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

     SEG LST:: flg: USED   lhd: 0x02c0000b ltl: 0x02c0000b

    End dump data blocks tsn: 14 file#: 11 minblk 9 maxblk 9

    *** 2004-06-14 10:47:50.000

    Start dump data blocks tsn: 14 file#: 11 minblk 9 maxblk 9

    buffer tsn: 14 rdba: 0x02c00009 (11/9)

    scn: 0x0000.01503350 seq: 0x02 flg: 0x00 tail: 0x33501002

    frmt: 0x02 chkval: 0x0000 type: 0x10=DATA SEGMENT HEADER - UNLIMITED

     Extent Control Header

     -----------------------------------------------------------------

     Extent Header:: spare1: 0      spare2: 0      #extents: 2      #blocks: 9    

                      last map 0x00000000 #maps: 0      offset: 4128 

          Highwater:: 0x02c0000d ext#: 0      blk#: 3      ext size: 4    

     #blocks in seg. hdr's freelists: 1    

     #blocks below: 3    

     mapblk 0x00000000 offset: 0    

                       Unlocked

         Map Header:: next 0x00000000 #extents: 2    obj#: 32387 flag: 0x40000000

     Extent Map

     -----------------------------------------------------------------

       0x02c0000a length: 4    

       0x02c0000e length: 5    

     

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

     SEG LST:: flg: USED   lhd: 0x02c0000c ltl: 0x02c0000c

    End dump data blocks tsn: 14 file#: 11 minblk 9 maxblk 9

    *** 2004-06-14 10:48:04.000

    Start dump data blocks tsn: 14 file#: 11 minblk 9 maxblk 9

    buffer tsn: 14 rdba: 0x02c00009 (11/9)

    scn: 0x0000.015033a4 seq: 0x02 flg: 0x00 tail: 0x33a41002

    frmt: 0x02 chkval: 0x0000 type: 0x10=DATA SEGMENT HEADER - UNLIMITED

     Extent Control Header

     -----------------------------------------------------------------

     Extent Header:: spare1: 0      spare2: 0      #extents: 2      #blocks: 9    

                      last map 0x00000000 #maps: 0      offset: 4128 

          Highwater:: 0x02c0000e ext#: 0      blk#: 4      ext size: 4    

     #blocks in seg. hdr's freelists: 1    

     #blocks below: 4    

     mapblk 0x00000000 offset: 0    

                       Unlocked

         Map Header:: next 0x00000000 #extents: 2    obj#: 32387 flag: 0x40000000

     Extent Map

     -----------------------------------------------------------------

       0x02c0000a length: 4    

       0x02c0000e length: 5    

     

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

     SEG LST:: flg: USED   lhd: 0x02c0000d ltl: 0x02c0000d

    End dump data blocks tsn: 14 file#: 11 minblk 9 maxblk 9

    *** 2004-06-14 10:50:20.000

    Start dump data blocks tsn: 14 file#: 11 minblk 9 maxblk 9

    buffer tsn: 14 rdba: 0x02c00009 (11/9)

    scn: 0x0000.0150350e seq: 0x03 flg: 0x00 tail: 0x350e1003

    frmt: 0x02 chkval: 0x0000 type: 0x10=DATA SEGMENT HEADER - UNLIMITED

     Extent Control Header

     -----------------------------------------------------------------

     Extent Header:: spare1: 0      spare2: 0      #extents: 2      #blocks: 9    

                      last map 0x00000000 #maps: 0      offset: 4128 

          Highwater:: 0x02c00013 ext#: 1      blk#: 5      ext size: 5    

     #blocks in seg. hdr's freelists: 5    

     #blocks below: 9    

     mapblk 0x00000000 offset: 1    

                       Unlocked

         Map Header:: next 0x00000000 #extents: 2    obj#: 32387 flag: 0x40000000

     Extent Map

     -----------------------------------------------------------------

       0x02c0000a length: 4    

       0x02c0000e length: 5    

     

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

     SEG LST:: flg: USED   lhd: 0x02c0000e ltl: 0x02c00012

    End dump data blocks tsn: 14 file#: 11 minblk 9 maxblk 9

    分析一下这个结果:

    Highwater:0x02c0000b à Highwater: 0x02c0000c à Highwater: 0x02c0000d

    à Highwater: 0x02c0000e à Highwater: 0x02c00013

    当我们没有设置_bump_highwater_mark_count,在前五个数据块,HWM是以1为步长移动的;在五块以后,HWM是以5为步长移动的。

    对于ASSM来说,情况又是不一样的。

    对于 extents <= 16 blocks的情况,HWM 移动遵循:

    第一次移动----à extent blocks - metadata

    第二次移动----à extent blocks

    对于 extents > 16 blocks的情况,HWM移动遵循:

    每次移动32blocks,但是HWM包含未格式化的block,每次格式化16block或者16 -metadata blocks

    我们在这里只是提出这样的问题让大家注意,不对ASSM的问题进行专门的讨论。

    四、HWM对性能的影响

    我们对一个table进行DML操作,主要是insertupdatedelete这三种。当一个table进行了多次的insert数据时,前面我们已经讨论了,tableHWM会不停地提升。现在我们来这样一种情况:如果在这期间我们对这个table进行了大量的delete操作,这是tableHWM会不会随着数据量的减少而下降呢?我们将通过一个实现来说明这个问题:

    这里我们要先引入一个procedure(转自tom的《oracle高级专家编程》)

    create or replace procedure show_space

    ( p_segname in varchar2,

     p_owner   in varchar2 default user,

     p_type    in varchar2 default 'TABLE',

     p_partition in varchar2 default NULL )

    as

        l_total_blocks              number;

        l_total_bytes               number;

        l_unused_blocks             number;

        l_unused_bytes              number;

        l_LastUsedExtFileId         number;

        l_LastUsedExtBlockId        number;

        l_last_used_block           number;

        procedure p( p_label in varchar2, p_num in number )

        is

        begin

            dbms_output.put_line( rpad(p_label,40,'.') ||

                                  p_num );

        end;

    begin

      

        dbms_space.unused_space

        ( segment_owner     => p_owner,

          segment_name      => p_segname,

          segment_type      => p_type,

              partition_name    => p_partition,

          total_blocks      => l_total_blocks,

          total_bytes       => l_total_bytes,

          unused_blocks     => l_unused_blocks,

          unused_bytes      => l_unused_bytes,

          last_used_extent_file_id => l_LastUsedExtFileId,

          last_used_extent_block_id => l_LastUsedExtBlockId,

          last_used_block => l_last_used_block );

        p( 'Total Blocks', l_total_blocks );

        p( 'Total Bytes', l_total_bytes );

        p( 'Unused Blocks', l_unused_blocks );

        p( 'Unused Bytes', l_unused_bytes );

        p( 'Last Used Ext FileId', l_LastUsedExtFileId );

        p( 'Last Used Ext BlockId', l_LastUsedExtBlockId );

        p( 'Last Used Block', l_last_used_block );

    end;

    /

    通过这个procedure显示的结果,我们可以得到一个segmentHWM的位置。在sqlplus中,我们要看到这个procedure显示的结果,需要设置: set serveroutput on

    这里,HWM = total_blocks - Unused Blocks +1

    我们来看这样一个实验:

    使用系统视图all_objects来创建测试table MY_OBJECTS,然后insert 31007行数据:

    SQL> create table MY_OBJECTS as

     2 select * from all_objects;

    Table created

    SQL> select count(*) from MY_OBJECTS;

     COUNT(*)

    ----------

         31007

    SQL> exec show_space(p_segname=>'MY_OBJECTS',p_owner =>'DLINGER',p_type => 'TABLE');

    Total Blocks............................425

    Total Bytes.............................3481600

    Unused Blocks...........................3

    Unused Bytes............................24576

    Last Used Ext FileId....................11

    Last Used Ext BlockId...................439

    Last Used Block.........................2

    这时,我们使用show_space来计算table MY_OBJECTSHWM,这里

    HWM=425 - 3 + 1 = 423 ;

    我们现在对table MY_OBJECTS 进行delete操作,删除前15000行数据:

    SQL> delete from MY_OBJECTS where rownum <15000;

    已删除14999行。

    SQL> exec show_space(p_segname => 'MY_OBJECTS',p_owner => 'DLINGER',p_type => 'TABLE');

    Total Blocks............................425

    Total Bytes.............................3481600

    Unused Blocks...........................3

    Unused Bytes............................24576

    Last Used Ext FileId....................11

    Last Used Ext BlockId...................439

    Last Used Block.........................2

    PL/SQL 过程已成功完成。

    现在我们再来观察HWM的结果,可以看到:这里HWM=425 - 3 + 1 = 423

    HWM的位置并没有发生变化。这说明对table MY_OBJECTS 删除了14999行数据后,并不会改变HWM的位置。

    那么,HWM过高会对数据库的性能有什么样的影响呢?

    这里我们以全表扫描为例,来讨论HWM过高的不良影响。

    同样,我们也通过一个实验来看full table scandelete前后访问的block数量的情况:

    SQL> set autotrace traceonly

    SQL> select count(*) from MY_OBJECTS;

     COUNT(*)

    ----------

         31007

    Statistics

    ----------------------------------------------------------

    。。。

           422 physical reads

              0 redo size

            378 bytes sent via SQL*Net to client

            503 bytes received via SQL*Net from client

              2 SQL*Net roundtrips to/from client

    。。。

    这里,我们通过oracleautotrace来观察sql的执行情况。

    看看这个sql访问的block422 physical reads

    我们通过Statistics的内容,可以看到,在table MY_OBJECTS31007行数据的情况下,对table MY_OBJECTS 进行一次full table scanoracle需要访问了422block

    这里,我们发现full table scan时访问的block数和HWM之下的block数量是一致的。

    如果我们删除table MY_OBJECTS 的一部分数据后,那我们对table MY_OBJECTS进行一次full table scan需要访问的block会不会随着数据行数的减少而降低呢?

    我们delete 14999行数据,这是只剩16008行数据了:

    SQL> delete from MY_OBJECTS where rownum<15000;

    14999 rows deleted

    SQL> commit;

    Commit complete

    在这里,我们把oracleshutdown,然后在startup,以便清空cache中的数据。

    SQL> set autotrace traceonly

    SQL> select count(*) from MY_OBJECTS;

     COUNT(*)

    ----------

         16008

    Statistics

    ----------------------------------------------------------

    。。。

           422 physical reads

              0 redo size

            378 bytes sent via SQL*Net to client

            503 bytes received via SQL*Net from client

              2 SQL*Net roundtrips to/from client

    。。。

    通过上面的Statistics的内容,可以看到,table full scan仍然访问了422block

    当我没有delete14999行数据时,全表扫描需要访问31007行数据;而当delete14999行数据之后,全表扫描实际需要访问的数据行减少了,但是oracle访问的block数量并没有减少。这说明进行table full scan时,实际上是对HWM下所有的block进行访问。我们知道,访问的block数量越多,代表需要消耗的资源越多。那么,当一个table在进行了大量的delete操作后,或者说,当一个tableHWM之下的block上的数据不饱和时,我们应该考虑采用一些方法来降低该表的HWM,以减小table full scan时需要访问的block数量。

    五、何时该降低HWM

    Table包含两种空闲的block

    HWM之上的空闲block。我们运行analyze table后,这些空闲的blocks会在user_tables EMPTY_BLOCKS中被统计。这些空闲的blocks实际上是从来没有存储过数据的,我们可以用以下命令来释放这些空间:

    Alter table table_name deallocate unused;

    HWM之下的空闲block。当数据插入到一个block后,那么HWM就移动到这个block之上了。然后后续的操作又将这个block中的数据删除了,那么,这个block实际上是空闲的。但是这些blocks位于HWM之下,所以是不会出现在EMPTY_BLOCKS中的。那么,这样的block过多,是会影响性能的,就像前面我们讨论过table full scan 中看到的那样。

     

    我们同样用系统视图all_objects来创建测试table MY_OBJECTS,然后随意delete其中的一部分数据,然后我们在对table MY_OBJECTS进行分析,来观察现在这个tableHWM之下的数据分布状况。

     

    对于LMTFLM

    我们可以用这个方法来一个tableHWM有多少blocks是不包含数据的。:

    SQL> analyze table MY_OBJECTS compute statistics;

    Table analyzed

    SQL> select (1- a.num/ b.num_total)*100 as percent from

     2 (select count(distinct substr(rowid,1,15)) num from MY_OBJECTS)a ,

     3 (select BLOCKS - EMPTY_BLOCKS num_total from user_tables where table_name= 'MY_OBJECTS') b;

       PERCENT

    ----------

    24.8606346

    从上面的结果,我们可以看到,table MY_OBJECTSHWM下有24.86%blocks是不包含数据的。当这个值比较高的时时候,我们可以考虑用一些方法来释放HWM下的空闲blocks了。注意,这里一定要先对table进行分析。

    我们还可以考察这样一个指标:

    SQL>select NUM_ROWS*AVG_ROW_LEN/

    ((BLOCKS-EMPTY_BLOCKS)*((100-PCT_FREE)/100)*8192)*100 percnt

     2 from dba_tables where table_name = 'MY_OBJECTS';

        PERCNT

    ----------

    72.1461836

    这里,我们可以看到table MY_OBJECTS的平均blocks的数据充满度为72%。注意,这里我的环境下oracleblock_size8k,那么在不同的block_size下,我们应该修改上面的sql中的8192的数值。这里计算时已经除去的PCTFREE的部分,MY_OBJECTSPCTFREE10,那么block的平均数据充满度实际上是72%×90%= 64.8%

    如果table经常进行全表扫描,或范围扫描,那么当这个值比较低的时候,也应该考虑来合并HWM下的blocks,将空闲的block释放。

    对于ASSM:

    对于ASSMsegment来说,考察HWM下的blocks的空间使用状况相对要简单一些。在这里,我们可以使用这样一个procedure来得到tableblocks使用情况:

    create or replace procedure show_space_assm(

    p_segname in varchar2,

    p_owner in varchar2 default user,

    p_type in varchar2 default 'TABLE' )  

    as

    l_fs1_bytes number;

    l_fs2_bytes number;

    l_fs3_bytes number;

    l_fs4_bytes number;

    l_fs1_blocks number;

    l_fs2_blocks number;

    l_fs3_blocks number;

    l_fs4_blocks number;

    l_full_bytes number;

    l_full_blocks number;

    l_unformatted_bytes number;

    l_unformatted_blocks number; 

    procedure p( p_label in varchar2, p_num in number )

    is

    begin

    dbms_output.put_line( rpad(p_label,40,'.') ||p_num );

    end;

    begin

    dbms_space.space_usage(

    segment_owner      => p_owner,

    segment_name       => p_segname,

    segment_type       => p_type,

    fs1_bytes          => l_fs1_bytes,

    fs1_blocks         => l_fs1_blocks,

    fs2_bytes          => l_fs2_bytes,

    fs2_blocks         => l_fs2_blocks,

    fs3_bytes          => l_fs3_bytes,

    fs3_blocks         => l_fs3_blocks,

    fs4_bytes          => l_fs4_bytes,

    fs4_blocks         => l_fs4_blocks,

    full_bytes         => l_full_bytes,

    full_blocks        => l_full_blocks,

    unformatted_blocks => l_unformatted_blocks,

    unformatted_bytes => l_unformatted_bytes); 

    p('free space 0-25% Blocks:',l_fs1_blocks);

    p('free space 25-50% Blocks:',l_fs2_blocks);

    p('free space 50-75% Blocks:',l_fs3_blocks);

    p('free space 75-100% Blocks:',l_fs4_blocks);

    p('Full Blocks:',l_full_blocks);

    p('Unformatted blocks:',l_unformatted_blocks);

    end;

    /

    我们知道,在ASSM下,block的空间使用分为free space 0-25%25-50%50-75%70-100%full 这样5中情况,show_space_assm会对需要统计的table汇总这5中类型的block的数量。

    我们来看table HWM1的空间使用情况:

    SQL> exec show_space_assm('HWM1','DLINGER');

    free space 0-25% Blocks:.................0

    free space 25-50% Blocks:...............1

    free space 50-75% Blocks:...............0

    free space 75-100% Blocks:..............8

    Full Blocks:.....................................417

    Unformatted blocks:.........................0

    这个结果显示,table HWM1fullblock417个,free space 75-100% Block8个,free space 25-50% Block1个。当table HWM下的blocks的状态大多为free space

    较高的值时,我们考虑来合并HWM下的blocks,将空闲的block释放,降低tableHWM

    六、如何降低HWM

    oracle8i以前的版本,如果我们需要降低segment HWM,可以采用两种方法:EXP/IMP CTAS,对这两种方法大家都很熟悉,我们在这里就不做讨论了。

    (1) Move

    8i开始,oracle开始提供Move的命令。我们通常使用这个命令,将一个table segment从一个tablespace移动到另一个tablespace

    Move实际上是在block之间物理的copy数据,那么,我们可以通过这种方式来降低tableHWM。我们先通过一个实验来看看move是如何移动数据的。创建table TEST_HWMinsert一些数据:

    SQL> create table TEST_HWM (id int ,name char(2000)) tablespace hwm;

    Table created

    我们往table TEST_HWM insert如下数据:

    insert into TEST_HWM values (1,'aa');

    insert into TEST_HWM values (2,'bb');

    insert into TEST_HWM values (2,'cc');

    insert into TEST_HWM values (3,'dd');

    insert into TEST_HWM values (4,'ds');

    insert into TEST_HWM values (5,'dss');

    insert into TEST_HWM values (6,'dss');

    insert into TEST_HWM values (7,'ess');

    insert into TEST_HWM values (8,'es');

    insert into TEST_HWM values (9,'es');

    insert into TEST_HWM values (10,'es');

    我们来看看这个tablerowidblockID和信息:

    SQL> select rowid , id,name from TEST_HWM;

                                                     

    ROWID                           ID     NAME

    -------------------------          ----------- ------

    AAAH7JAALAAAAAUAAA                1     aa

    AAAH7JAALAAAAAUAAB                2     bb

    AAAH7JAALAAAAAUAAC                2     cc

    AAAH7JAALAAAAAVAAA                3     dd

    AAAH7JAALAAAAAVAAB                4     ds

    AAAH7JAALAAAAAVAAC                5     dss

    AAAH7JAALAAAAAWAAA                6     dss

    AAAH7JAALAAAAAWAAB                7     ess

    AAAH7JAALAAAAAWAAC                8     es

    AAAH7JAALAAAAAXAAA                9     es

    AAAH7JAALAAAAAXAAB               10     es

    SQL> select EXTENT_ID,FILE_ID,RELATIVE_FNO,BLOCK_ID,BLOCKS

     2 from dba_extents where segment_name='TEST_HWM' ;

     EXTENT_ID    FILE_ID RELATIVE_FNO   BLOCK_ID     BLOCKS

    ---------- ---------- ------------ ---------- ----------

             0         11           11         19          5

    这里,简单地介绍一下rowid的相关知识:

    ROWID 磁盘10 个字的存储空间使用18 个字符来显示它包含下列组件:

     数据对象编号:个数据对象如表或索引在创建时分配,并且此编号在数据库中的;

    相关文件编号:此编号对个表空间中的个文件的;

    编号:表示包含此行的块在文件中的位置;

    编号:中行目位置的位置;

    在内部数据对象编号32 位,相关文件编号10 位,块编号22位行编号16 位,加80 位或10 个字节,ROWID 使用以64 基数的编码方案来显方案个位置用于数据对象,编号个位置用于相关文件编号个位置用于块编号个位置用于行编号64 基数的编码方案使用字A-Z a-z 0-9 + /64 个字符,示:

    AAAH7J AAL AAAAAU AAA

    在本例中

    AAAH7J   数据对象编号

    AAL      相关文件编号

    AAAAAU  编号

    AAA       编号

    那么,我们根据数据的rowid,可以看出这11行数据分布在AAAAAUAAAAAVAAAAAWAAAAAX这四个block中。

    然后我们从table TEST_HWMdelete一些数据:

    delete from TEST_HWM where id = 2;

    delete from TEST_HWM where id = 4;

    delete from TEST_HWM where id = 3;

    delete from TEST_HWM where id = 7;

    delete from TEST_HWM where id = 8;

    delete from TEST_HWM where id = 9;

    我们在来看看这个tablerowidblockID和信息:

    SQL> select rowid , id,name from TEST_HWM;

    ROWID                      ID NAME

    ------------------ ---------- --------- --------------

    AAAH7JAALAAAAAUAAA       1  aa

    AAAH7JAALAAAAAVAAC       5  dss

    AAAH7JAALAAAAAWAAA       6  dss

    AAAH7JAALAAAAAXAAB       10  es

    SQL> select EXTENT_ID,FILE_ID,RELATIVE_FNO,BLOCK_ID,BLOCKS

     2   from dba_extents where segment_name='TEST_HWM' ;

     EXTENT_ID    FILE_ID RELATIVE_FNO   BLOCK_ID     BLOCKS

    ---------- ---------- ------------ ---------- ----------

             0         11           11         19          5

    在这里,我们可以看到,数据的rowid没有发生改变,我们根据数据的rowid,可以看出这4行数据依然分布在AAAAAUAAAAAVAAAAAWAAAAAX这四个block中。

    接下来我们对table TEST_HWM进行move的操作,然后再来观察rowidblockid的信息:

    SQL> alter table TEST_HWM move;

    Table altered

    SQL> select rowid,id,name from HWM;

    ROWID                       ID NAME

    ------------------ ---------- -------- ---------------

    AAAH7NAALAAAANrAAA       1 aa

    AAAH7NAALAAAANrAAB       5 dss

    AAAH7NAALAAAANrAAC       6 dss

    AAAH7NAALAAAANsAAA       10 es

    SQL> select EXTENT_ID,FILE_ID,RELATIVE_FNO,BLOCK_ID,BLOCKS

     2   from dba_extents where segment_name=' TEST_HWM ' ;

     EXTENT_ID    FILE_ID RELATIVE_FNO   BLOCK_ID     BLOCKS

    ---------- ---------- ------------ ---------- ----------

             0         11           11        874          5

    我们可以看到,对table TEST_HWM进行move后,该table所在blockid发生了改变,那么数据的rowid自然也发生了改变。从上面的结果,我们可以看到,现在table TEST_HWM 的数据分布在AAAANrAAAANs两个block中了。但是这四行数据的rowid的顺序来看,这四行数据在table中的存储顺序并没有发生改变。move是在block之间对于数据的物理copy

    我们再来看看move操作对于tableHWM的位置有什么变化,我们同样使用系统视图all_objects来创建测试table my_objects,然后delete9999行数据:

    SQL> create table my_objects tablespace HWM

    2      as select * from all_objects;

    SQL> delete from my_objects where rownum<10000;

    9999 rows deleted

    SQL> select count(*) from my_objects;

     COUNT(*)

    ----------

         21015

    SQL> exec show_space(p_segname => 'MY_OBJECTS',p_owner => 'DLINGER',p_type => 'TABLE');

    Total Blocks............................425

    Total Bytes.............................3481600

    Unused Blocks...........................3

    Unused Bytes............................24576

    Last Used Ext FileId....................11

    Last Used Ext BlockId...................1294

    Last Used Block.........................2

    这里HWM=425 - 3 + 1 = 423

    然后对table MY_OBJECTS进行move操作:

    SQL> alter table MY_OBJECTS move;

    表已更改。

    SQL> exec show_space(p_segname => 'MY_OBJECTS',p_owner => 'DLINGER',p_type => 'TABLE');

    Total Blocks............................290

    Total Bytes.............................2375680

    Unused Blocks...........................1

    Unused Bytes............................8192

    Last Used Ext FileId....................11

    Last Used Ext BlockId...................1584

    Last Used Block.........................4

    我们可以看到,table MY_OBJECTSHWM423移动到290tableHWM降低了!

    我们还可以使用别的方法来降低tableHWM,比如CTASinsert into 等,那么move操作对redo logo的写和其他的方式比较起来是相对较少的,我们在这里就不列出把具体的实验结果了,大家有兴趣的可以自己动手来证实一下。

    上面我们讨论了move的执行机制和如何使用move降低tableHWM,这里,我们补充说明move的另外一些用法,以及使用move时的一些要注意的问题。

     

    Move的一些用法

    以下是alter table move子句的完整语法,我们介绍其中的几点:

    MOVE [ONLINE] 
      [segment_attributes_clause] 
      [data_segment_compression]
     [index_org_table_clause]
     [ { LOB_storage_clause | varray_col_properties }
        [ { LOB_storage_clause | varray_col_properties } ]...
     ]
     [parallel_clause]

     

    a. 我们可以使用move将一个table从当前的tablespace上移动到另一个tablespace上,如:

    alter table t move tablespace tablespace_name;

    b. 我们还可以用move来改变table已有的block的存储参数,如:

    alter table t move storage (initial 30k next 50k);

    另外,move操作也可以用来解决table中的行迁移的问题。

    使用move的一些注意事项

    a.     table上的index需要rebuild

    在前面我们讨论过,move操作后,数据的rowid发生了改变,我们知道,index是通过rowidfetch数据行的,所以,table上的index是必须要rebuild的。

    SQL> create index i_my_objects on my_objects (object_id);

    Index created

    SQL> alter table my_objects move;

    Table altered

    SQL> select index_name,status from user_indexes where index_name='I_MY_OBJECTS';

    INDEX_NAME                     STATUS

    ------------------------------ --------

    I_MY_OBJECTS                   UNUSABLE

     

    从这里可以看到,当table MY_OBJECTS进行move操作后,该table 上的inedx的状态为UNUSABLE,这时,我们可以使用alter index I_MY_OBJECTS rebuild online的命令,对index I_MY_OBJECTS进行在线rebuild

    b.    move时对table的锁定

    当我们对table MY_OBJECTS进行move操作时,查询v$locked_objects视图可以发现,table MY_OBJECTS上加了exclusive lock

    SQL>select OBJECT_ID, SESSION_ID,ORACLE_USERNAME,LOCKED_MODE from v$locked_objects;

     OBJECT_ID SESSION_ID ORACLE_USERNAME    LOCKED_MODE

    ---------- ---------- ------------------ -----------

         32471          9 DLINGER                      6

    SQL> select object_id from user_objects where object_name = 'MY_OBJECTS';

     OBJECT_ID

    ----------

        32471

     

    这就意味着,table在进行move操作时,我们只能对它进行select的操作。反过来说,当我们的一个sessiontable进行DML操作且没有commit时,在另一个session中是不能对这个table进行move操作的,否则oracle会返回这样的错误信息:ORA-00054: 资源正忙,要求指定 NOWAIT

    c.     关于move时空间使用的问题:

    当我们使用alter table move来降低tableHWM时,有一点是需要注意的,这时,当前的tablespace中需要有1倍于table的空闲空间以供使用:

    SQL> CREATE TABLESPACE TEST1

      2  DATAFILE 'D:\ORACLE\ORADATA\ORACLE9I\TEST1.dbf' SIZE 5M

      3  UNIFORM SIZE 128K ;


    SQL> create table my_objects  tablespace test1 as select * from all_objects;

    表已创建。


    SQL> select bytes/1024/1024 from user_segments where segment_name='MY_OBJECTS';


    BYTES/1024/1024

    ---------------

              3.125

     

    SQL> alter table MY_OBJECTS move;


    alter table MY_OBJECTS move

                *

    ERROR 位于第 1 :

    ORA-01652: 无法通过16(在表空间TEST1中)扩展 temp


    SQL> ALTER DATABASE

      2  DATAFILE 'D:\ORACLE\ORADATA\ORACLE9I\TEST1.DBF' RESIZE  7M;


    数据库已更改。

    SQL> alter table MY_OBJECTS move;

    表已更改。

    (2) DBMS_REDEFINITION

    这个包是从oracle 9i开始引入的,用来作table的联机重组和重定义。我们可以通过这种方法在线地重组table,来移动table中的数据,降低HWM,修改table的存储参数,分区等等。

    这个操作要求table上有一个主键,并要求预先创建一个带有要求修改的存储参数的table,以便保存重新组织后的数据。保存重新组织的数据的tble叫临时表,它只在重新组织期间被使用,在操作完成后可以被删除。

        使用DBMS_REDEFINITION Package需要如下权限:

    Create any table

    alter any table

    drop any table

    lock any table

    select any table

    DBMS_REDEFINITION上执行操作

     

    使用DBMS_REDEFINITION重组table一般是这样几个步骤:

    a. 使用DBMS_REDEFINITION.CAN_REDEF_TABLE()验证所选择的table能够被重建;

    b. 创建空的临时表,确保这个临时表定义了主键;

    c. 使用DBMS_REDEFINITION.START_REDEF_TABLE()进行table的重组;

    d. 在临时表上创建触发器,索引和约束,一般来说,这些对象于源有表中的是一致的,但是名称必须不同。同时要确保所创建的所有外键约束不可用。在重组结束时,所有这些对象将替换定义在源表上的对象。

    e. 使用DBMS_REDEFINITION.FINISH_REDEF_TABLE()完成重组的过程。在这期间,源表将会lock较短的时间。

    f.删除临时表。

    在这里,我们只是简单第介绍如何使用DBMS_REDEFINITIONtable进行在线重组和重定义,关于这个package具体的使用方法和使用上的限制,可以查阅oracle的官方文档:

    http://tahiti.oracle.com/

     

    (3). Shrink

    10g开始,oracle开始提供Shrink的命令,假如我们的表空间中支持自动段空间管理 (ASSM),就可以使用这个特性缩小段,即降低HWM。这里需要强调一点,10g的这个新特性,仅对ASSM表空间有效,否则会报 ORA-10635: Invalid segment or tablespace type

    在第4部分,我们已经讨论过,如何考察在ASSMtable是否需要回收浪费的空间,这里,我们来讨论如和对一个ASSMsegment回收浪费的空间。

       同样,我们用系统视图all_objects来在tablespace ASSM上创建测试表my_objects,这一小节的内容,实验环境为oracle10.1.0.2

    SQL> select * from v$version;

    BANNER

    ----------------------------------------------------------------

    Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Prod

    PL/SQL Release 10.1.0.2.0 - Production

    CORE 10.1.0.2.0    Production

    TNS for 32-bit Windows: Version 10.1.0.2.0 - Production

    NLSRTL Version 10.1.0.2.0 - Production

    SQL> select TABLESPACE_NAME,BLOCK_SIZE,EXTENT_MANAGEMENT,

     2 ALLOCATION_TYPE, SEGMENT_SPACE_MANAGEMENT

     3 from dba_tablespaces where TABLESPACE_NAME = 'ASSM';

    TABLESPACE_NAME BLOCK_SIZE EXTENT_MANAGEMENT ALLOCATION_TYPE SEGMENT_SPACE_MANAGEMENT

    ---------------- ---------- ----------------- --------------- ------------------------

    ASSM                   8192 LOCAL             UNIFORM         AUTO

    SQL> create table my_objects tablespace assm

     2 as select * from all_objects;

    Table created

    然后我们随机地从table MY_OBJECTS中删除一部分数据:

    SQL> select count(*) from my_objects;

     COUNT(*)

    ----------

         47828

    SQL> delete from my_objects where object_name like '%C%';

    16950 rows deleted

    SQL> delete from my_objects where object_name like '%U%';

    4503 rows deleted

    SQL> delete from my_objects where object_name like '%A%';

    6739 rows deleted

    现在我们使用show_spaceshow_space_assm来看看my_objects的数据存储状况:

    SQL> exec show_space('MY_OBJECTS','DLINGER');

    Total Blocks............................680

    Total Bytes.............................5570560

    Unused Blocks...........................1

    Unused Bytes............................8192

    Last Used Ext FileId....................6

    Last Used Ext BlockId...................793

    Last Used Block.........................4

    PL/SQL 过程已成功完成。

    SQL> exec show_space_assm('MY_OBJECTS','DLINGER');

    free space 0-25% Blocks:................0

    free space 25-50% Blocks:...............205

    free space 50-75% Blocks:...............180

    free space 75-100% Blocks:..............229

    Full Blocks:............................45

    Unformatted blocks:.....................0

    PL/SQL 过程已成功完成。

    这里,table my_objectsHWM下有679block,其中,free space25-50%block205个,free space50-75%block180个,free space75-100%block229个,full spaceblock只有45个,这种情况下,我们需要对这个table的现有数据行进行重组。

    要使用assm上的shink,首先我们需要使该表支持行移动,可以用这样的命令来完成:

    alter table my_objects enable row movement;

    现在,就可以来降低my_objectsHWM,回收空间了,使用命令:

    alter table bookings shrink space;

    我们具体的看一下实验的结果:

    SQL> alter table my_objects enable row movement;

    表已更改。

    SQL> alter table my_objects shrink space;

    表已更改。

    SQL> exec show_space('MY_OBJECTS','DLINGER');

    Total Blocks............................265

    Total Bytes.............................2170880

    Unused Blocks...........................2

    Unused Bytes............................16384

    Last Used Ext FileId....................6

    Last Used Ext BlockId...................308

    Last Used Block.........................3

    PL/SQL 过程已成功完成。

    SQL> exec show_space_assm('MY_OBJECTS','DLINGER');

    free space 0-25% Blocks:................0

    free space 25-50% Blocks:...............1

    free space 50-75% Blocks:...............0

    free space 75-100% Blocks:..............0

    Full Blocks:............................249

    Unformatted blocks:.....................0

    PL/SQL 过程已成功完成。

     

    在执行玩shrink命令后,我们可以看到,table my_objectsHWM现在降到了264的位置,而且HWM下的block的空间使用状况,full spaceblock249个,free space 25-50% Block只有1个。

    我们接下来讨论一下shrink的实现机制,我们同样使用讨论move机制的那个实验来观察。

    SQL> create table TEST_HWM (id int ,name char(2000)) tablespace ASSM;

    Table created

    table test_hwm中插入如下的数据:

    insert into TEST_HWM values (1,'aa');

    insert into TEST_HWM values (2,'bb');

    insert into TEST_HWM values (2,'cc');

    insert into TEST_HWM values (3,'dd');

    insert into TEST_HWM values (4,'ds');

    insert into TEST_HWM values (5,'dss');

    insert into TEST_HWM values (6,'dss');

    insert into TEST_HWM values (7,'ess');

    insert into TEST_HWM values (8,'es');

    insert into TEST_HWM values (9,'es');

    insert into TEST_HWM values (10,'es');

    我们来看看这个tablerowidblockID和信息:

    SQL> select rowid , id,name from TEST_HWM;

    ROWID                      ID NAME

    ------------------ ---------- ----- ---------

    AAANhqAAGAAAAFHAAA        1 aa

    AAANhqAAGAAAAFHAAB        2 bb

    AAANhqAAGAAAAFHAAC        2 cc

    AAANhqAAGAAAAFIAAA         3 dd

    AAANhqAAGAAAAFIAAB         4 ds

    AAANhqAAGAAAAFIAAC         5 dss

    AAANhqAAGAAAAFJAAA         6 dss

    AAANhqAAGAAAAFJAAB         7 ess

    AAANhqAAGAAAAFJAAC         8 es

    AAANhqAAGAAAAFKAAA         9 es

    AAANhqAAGAAAAFKAAB         10 es

    11 rows selected

    SQL> select EXTENT_ID,FILE_ID,RELATIVE_FNO,BLOCK_ID,BLOCKS

     2 from dba_extents where segment_name='TEST_HWM' ;

    EXTENT_ID    FILE_ID RELATIVE_FNO   BLOCK_ID     BLOCKS

    ---------- ---------- ------------ ---------- ----------

             0          6            6        324          5

             1          6            6        329          5

     

    然后从table test_hwm中删除一些数据:

    delete from TEST_HWM where id = 2;
    delete from TEST_HWM where id = 4;
    delete from TEST_HWM where id = 3;
    delete from TEST_HWM where id = 7;
    delete from TEST_HWM where id = 8;

    观察table test_hwmrowidblockid的信息:

    SQL> select rowid , id,name from TEST_HWM;

    ROWID                      ID NAME

    ------------------ ---------- ----- --------

    AAANhqAAGAAAAFHAAA      1 aa

    AAANhqAAGAAAAFIAAC       5 dss

    AAANhqAAGAAAAFJAAA       6 dss

    AAANhqAAGAAAAFKAAA       9 es

    AAANhqAAGAAAAFKAAB       10 es

    SQL> select EXTENT_ID,FILE_ID,RELATIVE_FNO,BLOCK_ID,BLOCKS

     2   from dba_extents where segment_name='TEST_HWM' ;

    EXTENT_ID    FILE_ID RELATIVE_FNO   BLOCK_ID     BLOCKS

    ---------- ---------- ------------ ---------- ----------

             0          6            6        324          5

             1          6            6        329          5

     

    从以上的信息,我们可以看到,在table test_hwm中,剩下的数据是分布在AAAAFHAAAAFIAAAAFJAAAAFK这样四个连续的block中。

     

    SQL> exec show_space_assm('TEST_HWM','DLINGER');

    free space 0-25% Blocks:................0

    free space 25-50% Blocks:...............1

    free space 50-75% Blocks:...............3

    free space 75-100% Blocks:..............3

    Full Blocks:............................0

    Unformatted blocks:.....................0

     

    通过show_space_assm我们可以看到目前这四个block的空间使用状况,AAAAFHAAAAFIAAAAFJ上各有一行数据,我们猜测free space50-75%3block是这三个block,那么free space25-50%1block就是AAAAFK了,剩下free space 75-100% 3block,是HWM下已格式化的尚未使用的block。(关于assmhwm的移动我们前面已经详细地讨论过了,在extent不大于于16block时,是以一个extent为单位来移动的)

    然后,我们对table my_objects执行shtink的操作:

    SQL> alter table test_hwm enable row movement;

    Table altered

    SQL> alter table test_hwm shrink space;

    Table altered

    SQL> select rowid ,id,name from TEST_HWM;

    ROWID                       ID NAME

    ------------------ ---------- ------ -----------

    AAANhqAAGAAAAFHAAA      1 aa

    AAANhqAAGAAAAFHAAB      10 es

    AAANhqAAGAAAAFHAAD      9 es

    AAANhqAAGAAAAFIAAC       5 dss

    AAANhqAAGAAAAFJAAA       6 dss

    SQL> select EXTENT_ID,FILE_ID,RELATIVE_FNO,BLOCK_ID,BLOCKS

     2 from dba_extents where segment_name='TEST_HWM' ;

     EXTENT_ID    FILE_ID RELATIVE_FNO   BLOCK_ID     BLOCKS

    ---------- ---------- ------------ ---------- ----------

             0          6            6        324          5

             1          6            6        329          5

     

    当执行了shrink操作后,有意思的现象出现了。我们来看看oracle是如何移动行数据的,这里的情况和move已经不太一样了。我们知道,在move操作的时候,所有行的rowid都发生了变化,table所位于的block的区域也发生了变化,但是所有行物理存储的顺序都没有发生变化,所以我们得到的结论是,oracleblock为单位,进行了block间的数据copy。那么shrink后,我们发现,部分行数据的rowid发生了变化,同时,部分行数据的物理存储的顺序也发生了变化,而table所位于的block的区域却没有变化,这就说明,shrink只移动了table其中一部分的行数据,来完成释放空间,而且,这个过程是在table当前所使用的block中完成的。

     

    那么oracle具体移动行数据的过程是怎样的呢?我们根据这样的实验结果,可以来猜测一下:

    Oracle是以行为单位来移动数据的。Oracle从当前table存储的最后一行数据开始移动,从当前table最先使用的block开始搜索空间,所以,shrink之前,rownum10的那行数据(10,es),被移动到block AAAAFH上,写到(1,aa)这行数据的后面,所以(10,es)的rownumrowid同时发生改变。然后是(9,es)这行数据,重复上述过程。这是oracle从后向前移动行数据的大致遵循的规则,那么具体移动行数据的的算法是比较复杂的,包括向ASSMtableinsert数据使用block的顺序的算法也是比较复杂的,大家有兴趣的可以自己来研究,在这里我们不多做讨论。

    我们还可以在shrink table的同时shrink这个table上的index

    alter table my_objects shrink space cascade;

    同样地,这个操作只有当table上的index也是ASSM时,才能使用。

       

    Shrink的几点问题:

    a. shrinkindex是否需要rebuild

    因为shrink的操作也会改变行数据的rowid,那么,如果table上有index时,shrink tableindex会不会变为UNUSABLE呢?我们来看这样的实验,同样构建my_objects的测试表:

    create table my_objects  tablespace ASSM as select * from all_objects where rownum<20000;

    create index i_my_objects on my_objects (object_id);

    delete from my_objects where object_name like '%C%';

    delete from my_objects where object_name like '%U%';

    现在我们来shrink table my_objects

    SQL> alter table my_objects enable row movement;

    Table altered

    SQL> alter table my_objects shrink space;

    Table altered

    SQL> select index_name,status from user_indexes where index_name='I_MY_OBJECTS';

    INDEX_NAME                     STATUS

    ------------------------------ --------

    I_MY_OBJECTS                    VALID

    我们发现,table my_objects上的index的状态为VALID,估计shrink在移动行数据时,也一起维护了index上相应行的数据rowid的信息。我们认为,这是对于move操作后需要rebuild index的改进。但是如果一个table上的index数量较多,我们知道,维护index的成本是比较高的,shrink过程中用来维护index的成本也会比较高。

    b. shrink时对tablelock

    在对table进行shrink时,会对table进行怎样的锁定呢?当我们对table MY_OBJECTS进行shrink操作时,查询v$locked_objects视图可以发现,table MY_OBJECTS上加了row-X (SX) lock

    SQL>select OBJECT_ID, SESSION_ID,ORACLE_USERNAME,LOCKED_MODE from v$locked_objects;

     OBJECT_ID SESSION_ID ORACLE_USERNAME    LOCKED_MODE

    ---------- ---------- ------------------ -----------

         55422          153 DLINGER                      3

    SQL> select object_id from user_objects where object_name = 'MY_OBJECTS';

     OBJECT_ID

    ----------

         55422

    那么,当table在进行shrink时,我们对table是可以进行DML操作的。

    c. shrink对空间的要求

    我们在前面讨论了shrink的数据的移动机制,既然oracle是从后向前移动行数据,那么,shrink的操作就不会像move一样,shrink不需要使用额外的空闲空间。

     

    小结:我们在这一部分介绍了三种降低table HWM的方法,那么实际的环境中,我们选择move还是shrink,可以针对这几项的特性,考虑你的系统的情况,做出选择。

    七、其余几种会移动HWM的操作

    还有几种操作是会改变HWM的:insert appendtruncate

    还有一些方法也可以用来降低HWM,比如:exp/imp等,我们在这里不做讨论。

    (1).insert append

    当我们使用insert /*+ append */ into向一个table中插入数据时,oracle不会在HWM以下寻找空间,而是直接移动HWM,从EMPTY_BLOCKS中获得要使用的block空间,来满足这一操作的blocks的需要。

    我们来看一个实验:

    SQL> create table hwm as select * from all_objects;

    Table created

    SQL> select count(*) from hwm;

     COUNT(*)

    ----------

         31009

    SQL> delete from hwm;

    31009 rows deleted

    SQL> exec show_space(p_segname => 'HWM',p_owner => 'DLINGER',p_type => 'TABLE');

    Total Blocks............................425

    Total Bytes.............................3481600

    Unused Blocks...........................3

    Unused Bytes............................24576

    Last Used Ext FileId....................11

    Last Used Ext BlockId...................439

    Last Used Block.........................2

    我们往表hwm中先插入31009条数据,然后在delete掉所有的数据。前面我们讨论过,delete操作不会降低HWM,所以这时的HWM = 425 - 3 + 1 = 423

    下面,我们来比较一下insertinsert append的不同结果:

    我们先使用insert into向表HWM中插入1000行数据,结果HWM没有移动。

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

    999 rows inserted

    SQL> commit;

    Commit complete

    SQL> exec show_space(p_segname => 'HWM',p_owner => 'DLINGER',p_type => 'TABLE');

    Total Blocks............................425

    Total Bytes.............................3481600

    Unused Blocks...........................3

    Unused Bytes............................24576

    Last Used Ext FileId....................11

    Last Used Ext BlockId...................439

    Last Used Block.........................2

    然后我们delete掉所有的数据,再用insert append来作同样的操作。可以看到,使用append提示后,结果就不一样了。

    SQL> delete from hwm;

    999 rows deleted

    SQL> commit;

    Commit complete

    SQL> insert /*+ append */ into hwm select * from all_objects where rownum<1000;

    999 rows inserted

    SQL> commit;

    Commit complete

    SQL> exec show_space(p_segname => 'HWM',p_owner => 'DLINGER',p_type => 'TABLE');

    Total Blocks............................440

    Total Bytes.............................3604480

    Unused Blocks...........................3

    Unused Bytes............................24576

    Last Used Ext FileId....................11

    Last Used Ext BlockId...................459

    Last Used Block.........................2

    我们发现,往hwm中插入同样的999行数据,使用insert appendHWM = 440 - 3 + 1 = 438 HWM423移动到了438

    我们再来比较以下insertinsert append的性能,对HWM插入同样的10000条数据。

    构建表T

    SQL> create table t as select * from all_objects;

    Table created

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

    31010 rows inserted

    SQL> commit;

    Commit complete

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

    62020 rows inserted

    SQL> commit;

    Commit complete

    SQL> select count(*) from t;

     COUNT(*)

    ----------

        124040

    HWM插入数据:

    SQL> set timing on

    SQL> insert into hwm select * from t;

    124040 rows inserted

    已用时间: 00: 00: 02.93

    SQL> commit;

    Commit complete

    已用时间: 00: 00: 00.20

    SQL> insert /*+ append */ into hwm select * from t;

    124040rows inserted

    已用时间: 00: 00: 01.02

    SQL> commit;

    Commit complete

    已用时间: 00: 00: 00.30

    当使用insert来插入124040行数据时,使用了2.93sec;而使用insert append插入124040行数据时,只使用了1.02sec

    在这里,提一下使用append的一个需要注意的问题:

    当我们使用insert append时,oracle会生成表级的独占锁:

    SQL> select * from v$mystat where rownum <2;

           SID STATISTIC#      VALUE

    ---------- ---------- ----------

            13          0          1

    SQL> insert /*+ append */ into hwm select * from all_objects where rownum<1000;

    999 rows inserted ――我们在这里不作commit

    ――在另一个session中执行:

    QL> select * from v$mystat where rownum <2;

           SID STATISTIC#      VALUE

    ---------- ---------- ----------

            10          0          1

    SQL> insert into hwm select * from all_objects where rownum<10;

    ――这个session出现等待

    现在我们观察v$lock

    SQL> select SID,TYPE,ID1,ID2,LMODE,REQUEST,BLOCK from v$lock;

    SID TYPE        ID1        ID2      LMODE    REQUEST      BLOCK

    ---------- ---- ---------- ---------- ---------- ---------- ----------

    ......

    10 TM        32398          0          0          3          0

    13 TX        65579     22477          6          0          0

    13 TM        32398          0          6          0          1

                                                              --13阻塞了一个process

    SQL> select object_name from user_objects where object_id = '32398';

    OBJECT_NAME

    --------------------------------------------------------------------------------

    HWM

    Session 13HWM上加上了exclusiveTM锁,这时session 13 blockingsession 10

    这里我们是在LMT下的segment中做的测试。在ASSMappend锁表的情况同样存在(直到oracle10gASSM中依然如此)。

    (2).Truncate

    我们讨论truncate table,一般是和delete from table做比较。

    前面,我们已经讨论过delete不会降低HWM的问题,这里我们再来看一下truncate的情况:

    SQL> exec show_space(p_segname => 'HWM',p_owner => 'DLINGER',p_type => 'TABLE');

    Total Blocks............................3380

    Total Bytes.............................27688960

    Unused Blocks...........................18

    Unused Bytes............................147456

    Last Used Ext FileId....................11

    Last Used Ext BlockId...................5069

    Last Used Block.........................2

    PL/SQL 过程已成功完成。      

    --这里HWM = 3380 - 18 + 1= 3363

    SQL> truncate table HWM;

    表已截掉。

    SQL> exec show_space(p_segname => 'HWM',p_owner => 'DLINGER',p_type => 'TABLE');

    Total Blocks............................5

    Total Bytes.............................40960

    Unused Blocks...........................4

    Unused Bytes............................32768

    Last Used Ext FileId....................11

    Last Used Ext BlockId...................19

    Last Used Block.........................1

    PL/SQL 过程已成功完成。

    --执行truncateHWM = 5 - 4 + 1 = 2

    我们发现,truncate table之后,HWM又回到了1中我们看到的segment初始化状态下HWM的位置。

  • 相关阅读:
    Linux基础命令---mv
    Linux服务器---基础设置
    Linux基础命令---find
    Linux服务器配置---安装centos
    Linux基础命令---ls
    Linux基础命令---rmdir
    Linux基础命令---chown
    Linux基础命令---chmod
    Linux基础命令---chgrp
    Linux基础命令---ln
  • 原文地址:https://www.cnblogs.com/HondaHsu/p/2617135.html
Copyright © 2011-2022 走看看