Oracle中 HWM與資料庫效能的探討

OraFige發表於2011-12-26

本文討論的是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的位置。

Note: 本文出去:

http://www.eygle.com/archives/2011/11/oracle_hwm_tuning.html

僅供學習參考用!!!

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26464953/viewspace-713937/,如需轉載,請註明出處,否則將追究法律責任。

相關文章