Oracle 執行語句插入臨時表報表空間不足,在增加表空間到最大后依然報錯,經分析應該為創建的臨時表水位線未清除導致,查詢降低水位線方法如下:
原文地址:http://blog.itpub.net/26736162/viewspace-2139546/
Oracle 降低高水位線的方法
高水位(HIGH WARTER MARK,HWM)好比水庫中儲水的水位,用於描述數據庫中段的擴展方式。高水位對全表掃描方式有着至關重要的影響。當使用DELETE刪除表記錄時,高水位並不會下降,隨之導致的是全表掃描的實際開銷並沒有任何減少。
例如,首先新建一張空表,大小占用64K,然后插入數據直到表大小變為50G,此時使用DELETE刪除所有的數據並且提交,這個時候查詢表的大小的時候依然是50G,這就是因為表的高水位沒有釋放的緣故,而在這時如果使用“SELECT * FROM TABLE_NAME;”語句來查詢數據的話,那么查詢過程就會很慢,因為Oracle要執行全表掃描,從高水位下所有的塊都得去掃描,直到50G的所有塊全部掃描完畢。曾遇到一個同事使用DELETE刪除了一個很大的分區表,然后執行SELECT查詢很久都沒有結果,以為是數據庫HANG住了,其實這個問題是由於高水位的緣故。所以,表執行了TRUNCATE操作,再次SELECT的時候就可以很快返回結果了。
釋放表的高水位通常有如下幾種辦法:
(1)對表進行MOVE操作:ALTER TABLE TABLE_NAME MOVE;。若表上存在索引,則記得重建索引。
(2)對表進行SHRINK SPACE操作:ALTER TABLE TABLE_NAME SHRINK SPACE;,注意,在執行該指令之前必須開啟行移動:ALTER TABLE TABLE_NAME ENABLE ROW MOVEMENT;。該方法的優點是:在碎片整理結束后,表上相關的索引仍然有效,缺點是會產生大量的UNDO和REDO。
(3)復制要保留的數據到臨時表T,DROP原表,然后RENAME臨時表T為原表。
(4)exp/imp或expdp/impdp重構表。
(5)若表中沒有數據則直接使用TRUNCATE來釋放高水位。
如何找出系統中哪些表擁有高水位呢?這里給出兩種辦法,①比較表的行數和表的大小關系。如果行數為0,而表的當前占用大小減去初始化時的大小(INITIAL_EXTENT)后依然很大,那么說明該表有高水位。②行數和塊數的比率,即查看一個塊可以存儲多少行數據。如果一個塊存儲的行數少於5行甚至更少,那么說明有高水位。注意,這兩種方法都不是十分准確,需要再對查詢結果進行篩選。需要注意的是,在查詢表的高水位時,首先需要分析表,以得到最准確的統計信息。
下面給出用於查詢高水位的幾個SQL語句:
SELECT D.OWNER,
ROUND(D.NUM_ROWS / D.BLOCKS, 2),
D.NUM_ROWS,
D.BLOCKS,
D.TABLE_NAME,
ROUND((d.BLOCKS*8-D.INITIAL_EXTENT/1024)/1024) t_size
FROM DBA_TABLES D
WHERE D.BLOCKS > 10
AND ROUND(D.NUM_ROWS / D.BLOCKS, 2) < 5
AND d.OWNER NOT LIKE '%SYS%' ;
或:
SELECT OWNER,
SEGMENT_NAME TABLE_NAME,
SEGMENT_TYPE,
GREATEST(ROUND(100 * (NVL(HWM - AVG_USED_BLOCKS, 0) /
GREATEST(NVL(HWM, 1), 1)),
2),
0) WASTE_PER
FROM (SELECT A.OWNER OWNER,
A.SEGMENT_NAME,
A.SEGMENT_TYPE,
B.LAST_ANALYZED,
A.BYTES,
B.NUM_ROWS,
A.BLOCKS BLOCKS,
B.EMPTY_BLOCKS EMPTY_BLOCKS,
A.BLOCKS - B.EMPTY_BLOCKS - 1 HWM,
DECODE(ROUND((B.AVG_ROW_LEN * NUM_ROWS *
(1 + (PCT_FREE / 100))) / C.BLOCKSIZE,
0),
0,
1,
ROUND((B.AVG_ROW_LEN * NUM_ROWS *
(1 + (PCT_FREE / 100))) / C.BLOCKSIZE,
0)) + 2 AVG_USED_BLOCKS,
ROUND(100 *
(NVL(B.CHAIN_CNT, 0) / GREATEST(NVL(B.NUM_ROWS, 1), 1)),
2) CHAIN_PER,
B.TABLESPACE_NAME O_TABLESPACE_NAME
FROM SYS.DBA_SEGMENTS A, SYS.DBA_TABLES B, SYS.TS$ C
WHERE A.OWNER = B.OWNER
AND SEGMENT_NAME = TABLE_NAME
AND SEGMENT_TYPE = 'TABLE'
AND B.TABLESPACE_NAME = C.NAME)
WHERE GREATEST(ROUND(100 * (NVL(HWM - AVG_USED_BLOCKS, 0) /
GREATEST(NVL(HWM, 1), 1)),
2),
0) > 50
AND OWNER NOT LIKE '%SYS%'
AND BLOCKS > 100
ORDER BY WASTE_PER DESC;
最后再次提醒各位讀者,若表執行了大量的DELETE操作后,則最好回收一下表的高水位。
http://docs.oracle.com/cd/E11882_01/server.112/e40540/logical.htm#CNCPT89022
Segment Space and the High Water Mark
To manage space, Oracle Database tracks the state of blocks in the segment. The high water mark (HWM) is the point in a segment beyond which data blocks are unformatted and have never been used.
MSSM uses free lists to manage segment space. At table creation, no blocks in the segment are formatted. When a session first inserts rows into the table, the database searches the free list for usable blocks. If the database finds no usable blocks, then it preformats a group of blocks, places them on the free list, and begins inserting data into the blocks. In MSSM, a full table scan reads allblocks below the HWM.
ASSM does not use free lists and so must manage space differently. When a session first inserts data into a table, the database formats a single bitmap block instead of preformatting a group of blocks as in MSSM. The bitmap tracks the state of blocks in the segment, taking the place of the free list. The database uses the bitmap to find free blocks and then formats each block before filling it with data. ASSM spread out inserts among blocks to avoid concurrency issues.
Every data block in an ASSM segment is in one of the following states:
-
Above the HWM
These blocks are unformatted and have never been used.
-
Below the HWM
These blocks are in one of the following states:
-
Allocated, but currently unformatted and unused
-
Formatted and contain data
-
Formatted and empty because the data was deleted
-
Figure 12-23 depicts an ASSM segment as a horizontal series of blocks. At table creation, the HWM is at the beginning of the segment on the left. Because no data has been inserted yet, all blocks in the segment are unformatted and never used.
Figure 12-23 HWM at Table Creation
Description of "Figure 12-23 HWM at Table Creation"
| 段空間和高水位標記 |
| oracle數據庫通過跟蹤段中的塊狀態來管理空間。高水位標記(HWM)是段中的一個點,超過該點的數據塊是未格式化和未使用過的。 |
| MSSM使用空閑列表來管理段空間。在創建表時,段中的塊並未被格式化。當一個會話初次向表中插入行時,數據庫將搜索空閑列表來查找可用的塊。如果數據庫未找到可用的塊,那么它會預格式化一組塊,並將它們放置在空閑列表中,並開始將數據插入到塊中。在MSSM中,全表掃描會讀取HWM之下的所有塊。 |
| ASSM不使用空閑列表,所以必須以不同的方式管理空間。當會話初次向表中插入數據時,數據庫只格式化一個單一位圖塊,而不像在MSSM中那樣預格式化一組塊。位圖取代了空閑列表,用於跟蹤在段中的塊的狀態。數據庫使用位圖來查找可用的塊,然后在往塊寫入數據之前將其格式化。ASSM將插入操作分散到多個塊,以避免並發問題。 |
| 在一個ASSM段中的每個數據塊處於以下狀態之一: |
| l 在HWM之上 這些塊是未格式化的,且從未使用過。 l 在HWM之下 這些塊處於以下狀態之一: u 已分配,但當前未格式化且未使用 u 已格式化且包含數據 u 已格式化且為空,因為數據已被刪除 |
| 圖12-23將一個ASSM段描述為一系列水平的塊。在創建表時,HWM在左側段的開頭。因為還未插入數據,段中的所有塊都還是未格式化且從未使用過。 |
| 圖將12-23在創建表時的HWM |
Suppose that a transaction inserts rows into the segment. The database must allocate a group of blocks to hold the rows. The allocated blocks fall below the HWM. The database formats a bitmap block in this group to hold the metadata, but does not preformat the remaining blocks in the group.
In Figure 12-24, the blocks below the HWM are allocated, whereas blocks above the HWM are neither allocated or formatted. As inserts occur, the database can write to any block with available space. The low high water mark (low HWM) marks the point below which all blocks are known to be formatted because they either contain data or formerly contained data.
In Figure 12-25, the database chooses a block between the HWM and low HWM and writes to it. The database could have just as easily chosen any other block between the HWM and low HWM, or any block below the low HWM that had available space. In Figure 12-25, the blocks to either side of the newly filled block are unformatted.
The low HWM is important in a full table scan. Because blocks below the HWM are formatted only when used, some blocks could be unformatted, as in Figure 12-25. For this reason, the database reads the bitmap block to obtain the location of the low HWM. The database reads all blocks up to the low HWM because they are known to be formatted, and then carefully reads only the formatted blocks between the low HWM and the HWM.
Assume that a new transaction inserts rows into the table, but the bitmap indicates that insufficient free space exists under the HWM. In Figure 12-26, the database advances the HWM to the right, allocating a new group of unformatted blocks.
When the blocks between the HWM and low HWM are full, the HWM advances to the right and the low HWM advances to the location of the old HWM. As the database inserts data over time, the HWM continues to advance to the right, with the low HWM always trailing behind it. Unless you manually rebuild, truncate, or shrink the object, the HWM never retreats.
See Also:
-
Oracle Database Administrator's Guide to learn how to shrink segments online
-
Oracle Database SQL Language Reference for TRUNCATE TABLE syntax and semantics
假設一個事務將行插入到段中。數據庫必須分配一組塊來容納這些行。已分配的塊在HWM之下。數據庫格式化該組中的一個位圖塊來容納元數據,但不會預格式化組中其余的塊。
在圖12-24中,HWM之下的塊是已分配的,而HWM之上的塊是既未分配也未格式化的。插入發生時,數據庫可以寫入到任何具有可用空間的塊。由低位高水位標記(低HWM)標記一個點,該點之下的所有塊都已知是已格式化的,要么包含數據,或以前曾包含數據。
在圖12-25中,數據庫選定了HWM和低HWM之間的一個塊,並往其中寫入數據。數據庫也可能會隨意選擇HWM和低HWM之間的任何其他塊,或低HWM之下任何有可用空間的塊。圖12-25中,在新填充塊兩邊的塊都還是未格式化的。
低HWM在全表掃描中非常重要。因為HWM之下的塊只在被使用時才格式化,所以可能還有一些塊是未被格式化的,如圖12-25所示。鑒於此,數據庫讀取位圖塊,以獲取低HWM的位置。數據庫讀取低HWM之下的所有塊,因為已知它們是已格式化的,然后僅仔細讀取位於低 HWM 和 HWM 之間的已格式化塊。
假定一個新事務將行插入到該表,但位圖指示在HWM之下沒有足夠的可用空間。在圖12-26中,數據庫向右推進HWM,分配一組新的未格式化塊。
當HWM與低HWM之間的塊填滿時,HWM向右推進,而低HWM相應推進到舊的HWM的位置。數據庫不斷插入數據,隨着時間的推移,HWM繼續向右推進,而低HWM總尾隨其后。除非您手動重建、截斷、或縮小該對象,否則HWM從不倒退。
備注:
1)move不但可以重置水位線(HWM),解決松散表帶來的 IO 浪費,還可以解決表中的行遷移問題。
move表的話需要雙倍的空間,否則無法成功。move表可以通過重新安排數據文件的空間達到收縮數據文件的目的。
move表時,會產生exclusive lock 鎖,此時只能對表進行 select 操作。
move表之后,如果表上有索引,記得重建。
2)shrink表只對ASSM管理的表有效,相關命令有:
-----alter table TABLE_NAME shrink space; 整理碎片並回收空間
-----alter table TABLE_NAME shrink space compact; 只整理碎片,不回收空間
-----alter table TABLE_NAME shrink space cascate; 整理碎片回收空間,並連同表的級聯對象一起整理(比如索引)
能在線進行,不影響表上的DML操作,當然,並發的DML操作在shrink結束的時刻會出現短暫的block;
shrink的另外一個優點是在碎片整理結束后,表上相關的index仍然enable。
3)move的操作速度遠遠快於shrink 操作 ,不是一般的快,不是一個數量級的,而且shrink 會產生大量的undo 和redo 操作。
4)truncate是DDL操作,相當於刪表后重建。
5)還有其他的方法,如導出后再重新導入。
准備工作,創建一張表:
CREATE TABLE TEST2
(
ID NUMBER(10),
NAME VARCHAR2(32)
);
1、移動表:
SQL> begin
2 for i in 1..10000 loop
3 insert into test2 values(i,'bbb');
4 end loop;
5 commit;
6 end;
7 /
PL/SQL procedure successfully completed.
SQL> select TABLE_NAME,TABLESPACE_NAME,BLOCKS,EMPTY_BLOCKS from user_tables where table_name='TEST2';
TABLE_NAME TABLESPACE_NAME BLOCKS EMPTY_BLOCKS
------------------------------ ------------------------------ ---------- ------------
TEST2 LXM 244 12
SQL> delete test2;
100000 rows deleted.
SQL> alter table test2 move;
Table altered.
SQL> analyze table test2 compute statistics;
Table analyzed.
SQL> select TABLE_NAME,TABLESPACE_NAME,BLOCKS,EMPTY_BLOCKS from user_tables where table_name='TEST2';
TABLE_NAME TABLESPACE_NAME BLOCKS EMPTY_BLOCKS
------------------------------ ------------------------------ ---------- ------------
TEST2 LXM 0 8
2、收縮表:
SQL> begin
2 for i in 1..100000 loop
3 insert into test2 values(i,'kkk');
4 end loop;
5 commit;
6 end;
7 /
PL/SQL procedure successfully completed.
SQL> analyze table test2 compute statistics;
Table analyzed.
SQL> select TABLE_NAME,TABLESPACE_NAME,BLOCKS,EMPTY_BLOCKS from user_tables where table_name='TEST2';
TABLE_NAME TABLESPACE_NAME BLOCKS EMPTY_BLOCKS
------------------------------ ------------------------------ ---------- ------------
TEST2 LXM 244 12
SQL> delete test2;
100000 rows deleted.
SQL> alter table test2 shrink space;
alter table test2 shrink space
*
ERROR at line 1:
ORA-10636: ROW MOVEMENT is not enabled
SQL> alter table test2 enable row movement;
Table altered.
SQL> alter table test2 shrink space;
Table altered.
SQL> analyze table test2 compute statistics;
Table analyzed.
SQL> select TABLE_NAME,TABLESPACE_NAME,BLOCKS,EMPTY_BLOCKS from user_tables where table_name='TEST2';
TABLE_NAME TABLESPACE_NAME BLOCKS EMPTY_BLOCKS
------------------------------ ------------------------------ ---------- ------------
TEST2 LXM 1 7
3、truncate表
SQL> begin
2 for i in 1..100000 loop
3 insert into test2 values(i,'kkk');
4 end loop;
5 commit;
6 end;
7 /
PL/SQL procedure successfully completed.
SQL> analyze table test2 compute statistics;
Table analyzed.
SQL> select TABLE_NAME,TABLESPACE_NAME,BLOCKS,EMPTY_BLOCKS from user_tables where table_name='TEST2';
TABLE_NAME TABLESPACE_NAME BLOCKS EMPTY_BLOCKS
------------------------------ ------------------------------ ---------- ------------
TEST2 LXM 244 12
SQL> truncate table test2;
Table truncated.
SQL> analyze table test2 compute statistics;
Table analyzed.
SQL> select TABLE_NAME,TABLESPACE_NAME,BLOCKS,EMPTY_BLOCKS from user_tables where table_name='TEST2';
TABLE_NAME TABLESPACE_NAME BLOCKS EMPTY_BLOCKS
------------------------------ ------------------------------ ---------- ------------
TEST2 LXM 0 8
4、新建臨時表,然后rename
SQL> begin
2 for i in 1..100000 loop
3 insert into test2 values(i,'kkk');
4 end loop;
5 commit;
6 end;
7 /
PL/SQL procedure successfully completed.
SQL> delete test2;
100000 rows deleted.
SQL> analyze table test2 compute statistics;
Table analyzed.
SQL> select TABLE_NAME,TABLESPACE_NAME,BLOCKS,EMPTY_BLOCKS from user_tables where table_name='TEST2';
TABLE_NAME TABLESPACE_NAME BLOCKS EMPTY_BLOCKS
------------------------------ ------------------------------ ---------- ------------
TEST2 LXM 244 12
SQL> create table test3 as select * from test2;
Table created.
SQL> drop table test2;
Table dropped.
SQL> alter table test3 rename to test2;
Table altered.
SQL> analyze table test2 compute statistics;
Table analyzed.
SQL> select TABLE_NAME,TABLESPACE_NAME,BLOCKS,EMPTY_BLOCKS from user_tables where table_name='TEST2';
TABLE_NAME TABLESPACE_NAME BLOCKS EMPTY_BLOCKS
------------------------------ ------------------------------ ---------- ------------
TEST2 LXM 0 0
==================================================
空表移動無須重建索引:
SQL> begin
2 for i in 1..10000 loop
3 insert into test2 values(i,'bbb');
4 end loop;
5 commit;
6 end;
7 /
PL/SQL procedure successfully completed.
SQL> select index_name,index_type,table_name,status,initial_extent,max_extents from user_indexes;
INDEX_NAME INDEX_TYPE TABLE_NAME STATUS INITIAL_EXTENT MAX_EXTENTS
------------------------------ --------------------------- ------------------------------ -------- -------------- -----------
IND_TEST2 NORMAL TEST2 VALID 65536 2147483645
SQL> delete test2 where id=1;
1 row deleted.
SQL> alter table test2 move;
Table altered.
SQL> select index_name,index_type,table_name,status,initial_extent,max_extents from user_indexes;
INDEX_NAME INDEX_TYPE TABLE_NAME STATUS INITIAL_EXTENT MAX_EXTENTS
------------------------------ --------------------------- ------------------------------ -------- -------------- -----------
IND_TEST2 NORMAL TEST2 UNUSABLE 65536 2147483645
SQL> alter index ind_test2 rebuild;
Index altered.
SQL> select index_name,index_type,table_name,status,initial_extent,max_extents from user_indexes;
INDEX_NAME INDEX_TYPE TABLE_NAME STATUS INITIAL_EXTENT MAX_EXTENTS
------------------------------ --------------------------- ------------------------------ -------- -------------- -----------
IND_TEST2 NORMAL TEST2 VALID 65536 2147483645
SQL> delete test2;
9999 rows deleted.
SQL> alter table test2 move;
Table altered.
SQL> select index_name,index_type,table_name,status,initial_extent,max_extents from user_indexes;
INDEX_NAME INDEX_TYPE TABLE_NAME STATUS INITIAL_EXTENT MAX_EXTENTS
------------------------------ --------------------------- ------------------------------ -------- -------------- -----------
IND_TEST2 NORMAL TEST2 VALID 65536 2147483645
高水位是記錄段里能容納數據的上限,高水位存在段里
全表掃先讀段頭塊,而后在段頭塊里面找到HWM
下面用實驗由內而外來理解Oracle的HWM
- --t表有一條數據
- hr@ORCL> select * from t;
- ID NAME
- ---------- ----------
- 1 AAAAA
- --找t段的段頭塊
- hr@ORCL> select header_file,header_block from dba_segments where segment_name='T' and owner='HR';
- HEADER_FILE HEADER_BLOCK
- ----------- ------------
- 4 387
- --另開一個session,dump段頭塊
- sys@ORCL> alter session set tracefile_identifier='sys_dump_t_01';
- Session altered.
- sys@ORCL> alter system dump datafile 4 block 387;
- System altered.
dump的部分trc內容摘入
- Extent Control Header
- -----------------------------------------------------------------
- Extent Header:: spare1: 0 spare2: 0 #extents: 1 #blocks: 8
- last map 0x00000000 #maps: 0 offset: 2716
- Highwater:: 0x01000189 ext#: 0 blk#: 8 ext size: 8 --Highwater就是高水位,0x01000189這個是HWM的地址
- #blocks in seg. hdr's freelists: 0
- #blocks below: 5
- mapblk 0x00000000 offset: 0
- Unlocked
- --------------------------------------------------------
- Low HighWater Mark :
- Highwater:: 0x01000189 ext#: 0 blk#: 8 ext size: 8
- #blocks in seg. hdr's freelists: 0
- #blocks below: 5
- mapblk 0x00000000 offset: 0
- Level 1 BMB for High HWM block: 0x01000181
- Level 1 BMB for Low HWM block: 0x01000181
- --------------------------------------------------------
- Segment Type: 1 nl2: 1 blksz: 8192 fbsz: 0
- L2 Array start offset: 0x00001434
- First Level 3 BMB: 0x00000000
- L2 Hint for inserts: 0x01000182
- Last Level 1 BMB: 0x01000181
- Last Level II BMB: 0x01000182
- Last Level III BMB: 0x00000000
- Map Header:: next 0x00000000 #extents: 1 obj#: 52713 flag: 0x10000000
- Inc # 0
- Extent Map
- -----------------------------------------------------------------
- 0x01000181 length: 8
- Auxillary Map
- --------------------------------------------------------
- Extent 0 : L1 dba: 0x01000181 Data dba: 0x01000184
- --------------------------------------------------------
- Second Level Bitmap block DBAs
- --------------------------------------------------------
- DBA 1: 0x01000182
- d dump data blocks tsn: 4 file#: 4 minblk 387 maxblk 387
- --對t表做一次全表掃
- hr@ORCL> set autot traceonly
- hr@ORCL> select * from t;
- Execution Plan
- ----------------------------------------------------------
- Plan hash value: 1601196873
- --------------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
- --------------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | 1 | 20 | 3 (0)| 00:00:01 |
- | 1 | TABLE ACCESS FULL| T | 1 | 20 | 3 (0)| 00:00:01 |
- --------------------------------------------------------------------------
- Note
- -----
- - dynamic sampling used for this statement
- Statistics
- ----------------------------------------------------------
- 0 recursive calls
- 0 db block gets
- 7 consistent gets --全表掃讀了6個塊
- 0 physical reads
- 0 redo size
- 469 bytes sent via SQL*Net to client
- 385 bytes received via SQL*Net from client
- 2 SQL*Net roundtrips to/from client
- 0 sorts (memory)
- 0 sorts (disk)
- 1 rows processed
這6個塊是如何算出來的呢?
- hr@ORCL> select file_id,block_id,blocks from dba_extents where segment_name='T';
- FILE_ID BLOCK_ID BLOCKS
- ---------- ---------- ----------
- 4 385 8
這t段一共用了8個塊,分別是385 386 387 388 389 390 391 392 393
Highwater:: 0x01000189 即:4號文件的393號塊
這個可由下面dbms_utility包算出
- sys@ORCL> select to_number('01000189','xxxxxxxx') from dual;
- TO_NUMBER('01000189','XXXXXXXX')
- --------------------------------
- 16777609
- sys@ORCL> select dbms_utility.data_block_address_file(16777609) from dual;
- DBMS_UTILITY.DATA_BLOCK_ADDRESS_FILE(16777609)
- ----------------------------------------------
- 4
- sys@ORCL> select dbms_utility.data_block_address_block(16777609) from dual;
- DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(16777609)
- -----------------------------------------------
- 393
讀了一次段頭塊:4號文件387號塊
讀了高水位之下的388 389 390 391 392 等5個塊
這樣一共就讀了6個塊
注:
385是FIRST LEVEL BITMAP BLOCK
386是SECOND LEVEL BITMAP BLOCK
接着分析我們所dump的內容:
- Low HighWater Mark :
- Highwater:: 0x01000189 ext#: 0 blk#: 8 ext size: 8
接下來談談highwater mark 和 low highwater mark
low high water mark與high water mark 之間可能存在formated block也可以可能存在unformatted block
先來理清dba_tables里面的字段blocks的含義
dba_tables.blocks記錄的是分析得到的 formatted block 的總數
而 low hwm 和 high hwm之間可能同時存在 formatted block 和 unfomatted block
所以准確地說 blocks 不能代表 low hwm 或high hwm
如果 low hwm 和 high hwm之間正好沒有formatted block時,dba_tables.blocks和low hwm下的blocks一致
那么什么是Oracle中未格式化的塊呢?
未格式化,意思就是這個塊,已經是屬於這個段了,但是還保留着原來的樣子沒動
格式化就是把塊中的數據清除掉,並把塊頭改為這個對象的
MSSM表空間中的段,只有一個高水位,高水位下的塊都是格式化了的
但是ASSM表空間中的段,有兩個高水位:低高水位和高高水位
即上文trc里的:Highwater:: 0x01000189和Low HighWater Mark Highwater:: 0x01000189
低高水位下的塊全部是格式化了的
但是低高水位和高高水位之間的塊,則可能是格式化了的,也可能是沒有
現在的t的高高水位是file 4,block 393;其低高水位是file 4,block 393
我們現在再來看一下t現在data_object_id是多少:
- hr@ORCL> select object_id,data_object_id from dba_objects where object_name='T';
- OBJECT_ID DATA_OBJECT_ID
- ---------- --------------
- 52713 52714
這里很明顯t的data_object_id大於object_id
也就是說,在t上曾經發生過move或truncate操作
注意,對於truncate操作而言,truncate后其data_object_id不一定就是在原先的data_object_id上加1
- sys@ORCL> select to_char('52714','XXXXXXXX') from dual;
- TO_CHAR('
- ---------
- CDEA
換句話說,t中現在在其低高水位和其高高水位之間的block,只要這個block上記錄的data_object_id不等於CDEA
我們可以通過dump里面的Block header dump部分中的seg/obj來判斷其data_object_id是否與段編號相等
那么這個block 就是一個未格式化的塊
也就是說,可以通過data_object_id來確定塊是在HWM和LHWM的位置
那么Oracle為什么要增加低高水位設置?出於什么目的?全表掃描時,是到低高水位,還是到高高水位?
Oracle設計有一個原則,就是把一個大操作分散到很多小操作中,以保證某個大操作不會花費太長時間
無論是延遲提交,還是什么,都體現了這種思想,這和Linux的理念有異曲同工之妙哦
而低高水位線與高高水位線結合,正是這種思想的體現
可以不用一次性格式化所有新分配的塊,留一部分給下次操作來完成
全表掃描時,通常都是讀至低高水位線,然后根據位圖去讀低高與高高之間格式化過的塊,避開未格式化的塊
如何查看HWM?如何知道HWM下有多少空閑的空間?
最實用的方法就是dump segment_header,速度快,而且對應用沒有影響
而且,trc里面的"#blocks in seg. hdr's freelists:"可以告訴我們HWM下有多少空閑塊
這里需要注意,如果dba_segments.freelist_groups > 1,那么freelist不再segment header中
比如,freelist_group = 3 ,則你便要分別dump header后面的3個塊,來看每個group的freelist的個數
那么如何降低HWM呢?
① expdp/impdp
② 10G及以后的版本可以采用shrink,需要注意的是,表所在表空間須為自動段空間管理
alter table tab_name enable row movement;
alter table tab_name shrink space;
③ 使用move,不過在Move期間,會影響到DML語句,如UPDATE,也需要考慮空間問題
總之move會鎖表 而且是TM 另外還會影響index,所以,之后記得rebuild index
alter table move tab_name; 在當前表空間中move
alter table move tab_name tablespace tbs_name; 將其move到其他表空間
④ CTAS 然后rename,rebuild index
⑤ 在線重定義
等等.......
高水位(High Water Mark)的概念及高水位問題的解決
舉 個例子來說,當我們創建一個表:PT_SCHE_DETAIL時,ORACLE就會為這個對象分配一個段.在這個段中,即使我們未插入任何記錄,也至少有 一個區被分配,第一個區的第一個塊就稱為段頭(SEGMENT HEADE),段頭中就儲存了一些信息,基中HWM的信息就存儲在此.此時,因為第一個區的第一塊用於存儲段頭的一些信息,雖然沒有存儲任何實際的記錄, 但也算是被使用,此時HWM是位於第2個塊.當我們不斷插入數據到PM_USER后,第1個塊已經放不下后面新插入的數據,此時,ORACLE將高水位之 上的塊用於存儲新增數據,同時,HWM本身也向上移.也就是說,當我們不斷插入數據時,HWM會往不斷上移,這樣,在HWM之下的,就表示使用過的 塊,HWM之上的就表示已分配但從未使用過的塊.
考慮讓我們看一個段,如一張表,其中填滿了塊,如圖 1 所示。在正常操作過程中,刪除了一些行,如圖 2 所示。現有就有了許多浪費的空間:(I) 在表的上一個末端和現有的塊之間,以及 (II) 在塊內部,其中還有一些沒有刪除的行。
圖2:行后面的塊已經刪除了;HWM 仍保持不變
HWM本身的信息是儲存在段頭.在段空間是手工管理方式時,ORACLE是通過FREELIST(一個單向鏈表)來管理段內的空間分配.在段空間是自動管理方式時(ASSM),ORACLE是通過BITMAP來管理段內的空間分配.
LOGGING
DATAFILE 'D:ORACLE_HOMEORADATARAINNYRAINNY.ORA' SIZE 5M
AUTOEXTEND
ON NEXT 10M MAXSIZE UNLIMITED EXTENT MANAGEMENT LOCAL
SEGMENT SPACE MANAGEMENT MANUAL;
I NUMBER(10);
FOR I IN 1..10000000 LOOP
INSERT INTO TEST_TAB VALUES(I,'TESTSTRING');
END LOOP;
COMMIT;
(C)我們來查詢一下,看在插入一千萬條記錄后所訪問的塊數和查詢所用時間:
SQL> SET TIMING ON
我們來看上面的執行計划,這句SQL總供耗時是:1分3秒.訪問方式是采用全表掃描方式(FTS),邏輯讀了156310個BLOCK,物理讀了154239個BLOCK.
我們來分析一下這個表:
DBMS_STATS.GATHER_TABLE_STATS(OWNNAME=> 'TEST',
TABNAME=> 'TEST_TAB',
PARTNAME=> NULL);END;
發現這個表目前使用的BLOCK有: 156532,未使用的BLOCK(EMPTY_BLOCKS)為:0,總行數為(NUM_ROWS):1000 0000
COMMIT;
----------------------------------------------------------
0 SELECT STATEMENT OPTIMIZER=CHOOSE (COST=15056 CARD=1)
2 1 TABLE ACCESS (FULL) OF 'TEST_TAB' (COST=15056 CARD=1)
----------------------------------------------------------
0 RECURSIVE CALLS
0 DB BLOCK GETS
156310 CONSISTENT GETS
155565 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
0 SORTS (MEMORY)
0 SORTS (DISK)
1 ROWS PROCESSED
我們在DELETE表后再次分析表,看看有什么變化:
這時, TEST_TAB表目前使用的BLOCK是: 156532,未使用的BLOCK(EMPTY_BLOCKS)為:0,總行數為(NUM_ROWS)已變成:0
問 題的根源就在於ORACLE的HWM.也就是說,在新增記錄時,HWM會慢慢往上移,但是在刪除記錄后,HWM卻不會往下移,也就是說,DELETE一千 萬條記錄后,此表的HWM根本沒移動,還在原來的那個位置,所以,HWM以下的塊數同樣也是一樣的.ORACLE的全表掃描是讀取ORACLE高水位標記 下的所有BLOCK,也就是說,不管HWM下的BLOCK現在實際有沒有存放數據,ORACLE都會一一讀取,這樣,大家可想而知,在我們DELETE表 后,ORACLE讀了大量的空塊,耗去了大量的時間.
我們再來看DELETE表后段空間實際使用的狀況:
TOTAL BYTES.............................1346371584
UNUSED BLOCKS...........................7168 --有7168塊沒有用過,也就是在HWM上面的塊數
UNUSED BYTES............................58720256
LAST USED EXT FILEID....................9
LAST USED EXT BLOCKID...................158856 -- BLOCK ID 是針對數據文件來編號的,表示最后使用的一個EXTENT的第一個BLOCK的編號
LAST USED BLOCK.........................1024 -- 在最后使用的一個EXTENT 中一共用了1024塊
LAST USED EXT BLOCK ID + LAST USED BLOCK -1 = HWM 所在的數據文件的BLOCK編號
TOTAL BYTES.............................1287651328
UNUSED BLOCKS...........................0
UNUSED BYTES............................0
LAST USED EXT FILEID....................9
LAST USED EXT BLOCKID...................158856
LAST USED BLOCK.........................1024
此時,總共用到的塊數已變為8, 我們再代入上面的公式,算出HWM的位置: 8-5=3 HWM所在的BLOCK ID是2632+3-1=2634,
-Recorded in the segment header block
-Set to the beginning of the segment on the creation
-Incremented in five-block increments as rows are inserted
-Reset by the truncate command
-Never reset by the delete command
-Space above the high-water-mark can be reclaimed at the table level by using the following command:
ALTER TABLE DEALLOCATE UNUSED…
----------------------------------------------------------
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (FULL) OF 'TEST_TAB'
----------------------------------------------------------
0 RECURSIVE CALLS
0 DB BLOCK GETS
0 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
0 SORTS (MEMORY)
0 SORTS (DISK)
1 ROWS PROCESSED
從 中我們也可以發現,分析表和SHOW_SPACE顯示的數據有點不一致.那么哪個是准的呢?其實這兩個都是准的,只不過計算的方法有點不同.事實上,當你 創建了一個對象如表以后,不管你有沒有插入數據,它都會占用一些塊,ORACLE也會給它分配必要的空間.同樣,用ALTER TABLE MOVE釋放自由空間后,還是保留了一些空間給這個表.
最后,我們再來執行TRUNCATE命令,截斷這個表,看看段空間的使用狀況:
TOTAL BYTES.............................65536
UNUSED BLOCKS...........................5
UNUSED BYTES............................40960
LAST USED EXT FILEID....................9
LAST USED EXT BLOCKID...................2632
LAST USED BLOCK.........................3
TOTAL BYTES.............................65536
UNUSED BLOCKS...........................5
UNUSED BYTES............................40960
LAST USED EXT FILEID....................9
LAST USED EXT BLOCKID...................2112
LAST USED BLOCK.........................3
(
INITIAL 64K
MINEXTENTS 1
MAXEXTENTS UNLIMITED
);
(2)如果MINEXTENT >HWM 則釋放MINEXTENTS 以上的空間。如果要釋放HWM以上的空間則使用KEEP 0。
ALTER TABLE TABLESNAME DEALLOCATE UNUSED KEEP 0;
(3) TRUNCATE TABLE DROP STORAGE(缺省值)命令可以將MINEXTENT 之上的空間完全釋放(交還給操作系統),並且重置HWM。
(4)如果僅是要移動HWM,而不想讓表長時間鎖住,可以用TRUNCATE TABLE REUSE STORAGE,僅將HWM重置。
(5)ALTER TABLE MOVE會將HWM移動,但在MOVE時需要雙倍的表空間,而且如果表上有索引的話,需要重構索引
(6)DELETE表不會重置HWM,也不會釋放自由的空間(也就是說DELETE空出來的空間只能給對象本身將來的INSERT/UPDATE使用,不能給其它的對象使用)
如果要同時壓縮表的索引,可以發布:ALTER TABLE TEST_TAB SHRINK SPACE CASCADE
注意:在使用此命令時需要先使行可遷移row movement(具體見例子)。
與使用ALTER TABLE MOVE 不同的是執行此命令后並不需要重構索引。
You use online segment shrink to reclaim fragmented free space below the high water mark in an Oracle Database segment. The benefits of segment shrink are these:
* Compaction of data leads to better cache utilization, which in turn leads to better online transaction processing (OLTP) performance.
* The compacted data requires fewer blocks to be scanned in full table scans, which in turns leads to better decision support system (DSS) performance.
Segment shrink is an online, in-place operation. DML operations and queries can be issued during the data movement phase of segment shrink. Concurrent DML operation are blocked for a short time at the end of the shrink operation, when the space is deallocated. Indexes are maintained during the shrink operation and remain usable after the operation is complete. Segment shrink does not require extra disk space to be allocated.
Segment shrink reclaims unused space both above and below the high water mark. In contrast, space deallocation reclaims unused space only above the high water mark. In shrink operations, by default, the database compacts the segment, adjusts the high water mark, and releases the reclaimed space.
Segment shrink requires that rows be moved to new locations. Therefore, you must first enable row movement in the object you want to shrink and disable any rowid-based triggers defined on the object.
Shrink operations can be performed only on segments in locally managed tablespaces with automatic segment space management (ASSM). Within an ASSM tablespace, all segment types are eligible for online segment shrink except these:
* IOT mapping tables
* Tables with rowid based materialized views
* Tables with function-based indexes
Elapsed: 00:00:05.83
----------
210992
Elapsed: 00:00:01.06
210992 rows created.
Elapsed: 00:00:59.83
Commit complete.
Elapsed: 00:00:00.07
Total Bytes.............................75497472
Unused Blocks...........................768
Unused Bytes............................6291456
Last Used Ext FileId....................4
Last Used Ext BlockId...................8328
Last Used Block.........................256
也可以通過查看extents得到HWM=8*16+128*63+256=8192+256=8448
Elapsed: 00:00:00.01
Elapsed: 00:00:40.99
Elapsed: 00:00:00.01
Total Bytes.............................75497472
Unused Blocks...........................768
Unused Bytes............................6291456
Last Used Ext FileId....................4
Last Used Ext BlockId...................8328
Last Used Block.........................256
Elapsed: 00:00:00.00
alter table demo shrink space
*
ERROR at line 1:
ORA-10636: ROW MOVEMENT is not enabled
Elapsed: 00:00:00.09
Elapsed: 00:00:00.10
Elapsed: 00:01:35.51
Total Bytes.............................29949952
Unused Blocks...........................0
Unused Bytes............................0
Last Used Ext FileId....................4
Last Used Ext BlockId...................3720
Last Used Block.........................72
Elapsed: 00:00:00.02
高水位線實驗:
-- 創建test3表
SQL> create table test3 as
2 select * from dba_objects where 1 = 2;
Table created
-- 查看表中分配塊,區大小
SQL> SELECT segment_name, segment_type, blocks -- 分配數據塊數, extents -- 分配區塊數
2 FROM dba_segments
3 WHERE segment_name = 'TEST3'
4 ;
SEGMENT_NAME SEGMENT_TYPE BLOCKS EXTENTS
-------------------------------------------------------------------------------- ------------------ ---------- ----------
TEST3 TABLE 8 1
TEST3 TABLE 8 1
-- 分析表TEST3表
SQL> ANALYZE TABLE TEST3 ESTIMATE STATISTICS;
Table analyzed
-- 查詢TEST3表高水位線
SQL> SELECT blocks -- 高水位線(占用TEST3表數據塊數), empty_blocks -- TEST3表空閑塊數, num_rows
2 FROM user_tables
3 WHERE table_name = 'TEST3';
BLOCKS EMPTY_BLOCKS NUM_ROWS
---------- ------------ ----------
0 7 0
-- 因為未向TEST3表中插入任何數據,因此此表的高水位線為0,現向TEST3表中插入數據再觀察
SQL> insert into test3
2 select * from dba_objects;
50361 rows inserted
SQL> commit;
Commit complete
-- 重新分析表
SQL> ANALYZE TABLE TEST3 ESTIMATE STATISTICS;
Table analyzed
-- 再次查看表中分配塊,區大小
SQL> SELECT segment_name, segment_type, blocks, extents
2 FROM dba_segments
3 WHERE segment_name = 'TEST3'
4 ;
SEGMENT_NAME SEGMENT_TYPE BLOCKS EXTENTS
-------------------------------------------------------------------------------- ------------------ ---------- ----------
TEST3 TABLE 8 1
TEST3 TABLE 768 21
此時看到BLOCKS數已增長到768, 也就是Oracle分配給TEST3表768個數據塊,21個區
-- 再次查看TEST3表高水位線
SQL> SELECT blocks, empty_blocks, num_rows
2 FROM user_tables
3 WHERE table_name = 'TEST3';
BLOCKS EMPTY_BLOCKS NUM_ROWS
---------- ------------ ----------
689 78 50361
已增長到689個塊, 還有78個空閑塊,689 + 78 = 767, 比分配的少1個數據塊,是因為這一個數據塊是用作segment header
-- 現將TEST3表delete,在查看高水位線
SQL> delete from test3;
50361 rows deleted
SQL> commit;
Commit complete
SQL> ANALYZE TABLE TEST3 ESTIMATE STATISTICS;
Table analyzed
SQL>
SQL> SELECT blocks, empty_blocks, num_rows
2 FROM user_tables
3 WHERE table_name = 'TEST3';
BLOCKS EMPTY_BLOCKS NUM_ROWS
---------- ------------ ----------
689 78 0
發現此表高水位線並未減少,證明delete只是刪除表中數據塊的記錄,但並不會使表中的高水位線下降, 在進行全表掃描時會Oracle會掃描表中高水位線下的所有數據塊,
因此數據雖然被刪除了,但查詢時有可能還是很慢。所以在進行大表刪除時應使用truncate語句,看下面實驗:
SQL> truncate table test3;
Table truncated
SQL> ANALYZE TABLE TEST3 ESTIMATE STATISTICS;
Table analyzed
SQL>
SQL> SELECT blocks, empty_blocks, num_rows
2 FROM user_tables
3 WHERE table_name = 'TEST3';
BLOCKS EMPTY_BLOCKS NUM_ROWS
---------- ------------ ----------
0 7 0
現在表中高水位下降到0了, 一點心得, 記錄下來。
4. 修正ORACLE 表的高水位線
在ORACLE 中,執行對表的刪除操作不會降低該表的高水位線。而全表掃描將始終讀取一個段(extent) 中所有低於高水位線標記的塊。如果在執行刪除操作后不降低高水位線標記,則將導致查詢語句的性能低下。
下面的方法都可以 降低高水位線標記 。
1. 執行表重建指令 alter table table_name move;
在線轉移表空間ALTER TABLE ... MOVE TABLESPACE ..
當你創建了一個對象如表以后, 不管你有沒有插入數據 , 它都會占用一些塊 ,ORACLE 也會給它分配必要的空間 . 同樣 , 用 ALTER TABLE MOVE 釋放自由空間后 , 還是保留了一些空間給這個表 .
ALTER TABLE ... MOVE 后面不跟參數也行,不跟參數表還是在原來的表空間, M ove后記住重建索引 . 如果以后還要繼續向這個表增加數據,沒有必要move , 只是釋放出來的空間,只能這個表用,其他的表或者 segment 無法使用該空間 。
2. 執行alter table table_name shrink space;
注意, 此命令為Oracle 10g 新增功能 ,再執行該指令之前必須允許行移動 alter table table_name enable row movement;
3. 復制要保留的數據到臨時表t , drop 原表,然后 rename 臨時表 t 為原表
4. 用邏輯導入導出: E mp/ I mp
5. A lter table table_name deallocate unused
注: 這證明,DEALLOCATE UNUSED 為釋放 HWM 上面的未使用空間 , 但是並不會釋放 HWM 下面的自由空間 , 也不會移動 HWM 的位置 .
6. 盡量使用 truncate .
注意:
在9I 中 :
1. 如果是 INEXTENT , 可以使ALTER TABLE TABLENAME DEALLOCATE UNUSED 將 HWM 以上所有沒使用的空間釋放
2. 如果MINEXTENT >HWM 則釋放 MINEXTENTS 以上的空間。如果要釋放 HWM 以上的空間則使用 KEEP 0 。
ALTER TABLE TABLESNAME DEALLOCATE UNUSED KEEP 0;
3. TRUNCATE TABLE DROP STORAGE (缺省值 ) 命令可以將MINEXTENT 之上的空間完全釋放 (交還給操作系統 ), 並且重置 HWM 。
4. 如果僅是要移動HWM, 而不想讓表長時間鎖住 , 可以用 TRUNCATE TABLE REUSE STORAGE, 僅將 HWM 重置。
5. ALTER TABLE MOVE會將 HWM 移動 , 但在 MOVE 時需要雙倍的表空間 , 而且如果表上有索引的話 , 需要重構索引
6. DELETE表不會重置 HWM, 也不會釋放自由的空間 ( 也就是說 DELETE 空出來的空間只能給對象本身將來的 INSERT/UPDATE 使用 , 不能給其它的對象使用 )
在ORACLE 10G:
1. 可以使用ALTER TABLE TEST_TAB SHRINK SPACE 命令來聯機移動 HWM,
2. 如果要同時壓縮表的索引, 可以發布 :ALTER TABLE TEST_TAB SHRINK SPACE CASCADE
5. HWM 特點:
1. ORACLE用 HWM 來界定一個段中使用的塊和未使用的塊 .
舉個例子來說, 當我們創建一個表時 ,ORACLE 就會為這個對象分配一個段 . 在這個段中 , 即使我們未插入任何記錄 , 也至少有一個區被分配 , 第一個區的第一個塊就稱為段頭 (SEGMENT HEADE), 段頭中就儲存了一些信息 , 基中 HWM 的信息就存儲在此 . 此時 , 因為第一個區的第一塊用於存儲段頭的一些信息 , 雖然沒有存儲任何實際的記錄 , 但也算是被使用 , 此時 HWM 是位於第 2 個塊 . 當我們不斷插入數據到 表 后, 第 1 個塊已經放不下后面新插入的數據 , 此時 ,ORACLE 將高水位之上的塊用於存儲新增數據 , 同時 ,HWM 本身也向上移 . 也就是說 , 當我們不斷插入數據時 ,HWM 會往不斷上移 , 這樣 , 在 HWM 之下的 , 就表示使用過的塊 ,HWM 之上的就表示已分配但從未使用過的塊 .
2. HWM在插入數據時 , 當現有空間不足而進行空間的擴展時會向上移 , 但刪除數據時不會往下移 .
這就好比是水庫的水位, 當漲水時 , 水位往上移 , 當水退出后 , 最高水位的痕跡還是清淅可見 .
ORACLE 不會釋放空間以供其他對象使用,有一條簡單的理由:由於空間是為新插入的行保留的,並且要適應現有行的增長。被占用的最高空間稱為最高使用標記 (HWM) ,
3. HWM的信息存儲在段頭當中 .
HWM本身的信息是儲存在段頭 . 在段空間是手工管理方式時 ,ORACLE 是通過 FREELIST( 一個單向鏈表 ) 來管理段內的空間分配 . 在段空間是自動管理方式時 (ASSM),ORACLE 是通過 BITMAP 來管理段內的空間分配 .
4. ORACLE的全表掃描是讀取高水位標記 (HWM) 以下的所有塊 .
所以問題就產生了. 當用戶發出一個全表掃描時, ORACLE 始終必須從段一直掃描到 HWM ,即使它什么也沒有發現。該任務延長了全表掃描的時間。
5. 當用直接路徑插入行時 , 即使HWM 以下有空閑的數據庫塊,鍵入在插入數據時使用了 append關鍵字,則在插入時使用 HWM 以上的數據塊,此時 HWM 會自動增大。
一、什么是水線(High Water Mark)?
所有的oracle段(segments,在此,為了理解方便,建議把segment作為表的一個同義詞) 都有一個在段內容納數據的上限,我們把這個上限稱為"high water mark"或HWM。這個HWM是一個標記,用來說明已經有多少沒有使用的數據塊分配給這個segment。HWM通常增長的幅度為一次5個數據塊,原則上HWM只會增大,不會縮小,即使將表中的數據全部刪除,HWM還是為原值,由於這個特點,使HWM很象一個水庫的歷史最高水位,這也就是HWM的原始含義,當然不能說一個水庫沒水了,就說該水庫的歷史最高水位為0。但是如果我們在表上使用了truncate命令,則該表的HWM會被重新置為0。
二、HWM數據庫的操作有如下影響:
a) 全表掃描通常要讀出直到HWM標記的所有的屬於該表數據庫塊,即使該表中沒有任何數據。
b) 即使HWM以下有空閑的數據庫塊,鍵入在插入數據時使用了append關鍵字,則在插入時使用HWM以上的數據塊,此時HWM會自動增大。
三、如何知道一個表的HWM?
a) 首先對表進行分析:
ANALYZE TABLE <tablename> ESTIMATE/COMPUTE STATISTICS;
b) SELECT blocks, empty_blocks, num_rows
FROM user_tables
WHERE table_name = <tablename>;
說明:
BLOCKS 列代表該表中曾經使用過得數據庫塊的數目,即水線。
EMPTY_BLOCKS 代表分配給該表,但是在水線以上的數據庫塊,即從來沒有使用的數據塊。
讓我們以一個有28672行的BIG_EMP1表為例進行說明:
1) SQL> SELECT segment_name, segment_type, blocks
FROM dba_segments
WHERE segment_name='BIG_EMP1';
SEGMENT_NAME SEGMENT_TYPE BLOCKS
----------------- -------------- ---------
BIG_EMP1 TABLE 1024
1 row selected.
2) SQL> ANALYZE TABLE big_emp1 ESTIMATE STATISTICS;
Statement processed.
3) SQL> SELECT table_name,num_rows,blocks,empty_blocks
FROM user_tables
WHERE table_name='BIG_EMP1';
TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS
---------- -------- ------- -------------
BIG_EMP1 28672 700 323
1 row selected.
注意:
BLOCKS + EMPTY_BLOCKS (700+323=1023)比DBA_SEGMENTS.BLOCKS少1個數據庫塊,這是因為有一個數據庫塊被保留用作segment header。DBA_SEGMENTS.BLOCKS 表示分配給這個表的所有的數據庫塊的數目。USER_TABLES.BLOCKS表示已經使用過的數據庫塊的數目。
4) SQL> SELECT COUNT (DISTINCT
DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid)||
DBMS_ROWID.ROWID_RELATIVE_FNO(rowid)) "Used"
FROM big_emp1;
Used
----------
700
1 row selected.
5) SQL> delete from big_emp1;
28672 rows processed.
6) SQL> commit;
Statement processed.
7) SQL> ANALYZE TABLE big_emp1 ESTIMATE STATISTICS;
Statement processed.
8) SQL> SELECT table_name,num_rows,blocks,empty_blocks
FROM user_tables
WHERE table_name='BIG_EMP1';
TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS
--------- -------- ------- ----------
BIG_EMP1 0 700 323
1 row selected.
9) SQL> SELECT COUNT (DISTINCT
DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid)||
DBMS_ROWID.ROWID_RELATIVE_FNO(rowid)) "Used"
FROM big_emp1;
Used
----------
0 ----這表名沒有任何數據庫塊容納數據,即表中無數據
1 row selected.
10) SQL> TRUNCATE TABLE big_emp1;
Statement processed.
11) SQL> ANALYZE TABLE big_emp1 ESTIMATE STATISTICS;
Statement processed.
12) SQL> SELECT table_name,num_rows,blocks,empty_blocks
2> FROM user_tables
3> WHERE table_name='BIG_EMP1';
TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS
---------- -------- -------- ------------
BIG_EMP1 0 0 511
1 row selected.
13) SQL> SELECT segment_name,segment_type,blocks
FROM dba_segments
WHERE segment_name='BIG_EMP1';
SEGMENT_NAME SEGMENT_TYPE BLOCKS
------------ ------------- ------
BIG_EMP1 TABLE 512
1 row selected.
注意:
TRUNCATE命令回收了由delete命令產生的空閑空間,注意該表分配的空間由原先的1024塊降為512塊。
為了保留由delete命令產生的空閑空間,可以使用TRUNCATE TABLE big_emp1 REUSE STORAGE.
用此命令后,該表還會是原先的1024塊。
四、Oracle表段中的高水位線HWM
在Oracle數據的存儲中,可以把存儲空間想象為一個水庫,數據想象為水庫中的水。水庫中的水的位置有一條線叫做水位線,在Oracle中,這條線被稱為高水位線(High-warter mark, HWM)。在數據庫表剛建立的時候,由於沒有任何數據,所以這個時候水位線是空的,也就是說HWM為最低值。當插入了數據以后,高水位線就會上漲,但是這里也有一個特性,就是如果你采用delete語句刪除數據的話,數據雖然被刪除了,但是高水位線卻沒有降低,還是你剛才刪除數據以前那么高的水位。也就是說,這條高水位線在日常的增刪操作中只會上漲,不會下跌。
下面我們來談一下Oracle中Select語句的特性。Select語句會對表中的數據進行一次掃描,但是究竟掃描多少數據存儲塊呢,這個並不是說數據庫中有多少數據,Oracle就掃描這么大的數據塊,而是Oracle會掃描高水位線以下的數據塊。現在來想象一下,如果剛才是一張剛剛建立的空表,你進行了一次Select操作,那么由於高水位線HWM在最低的0位置上,所以沒有數據塊需要被掃描,掃描時間會極短。而如果這個時候你首先插入了一千萬條數據,然后再用delete語句刪除這一千萬條數據。由於插入了一千萬條數據,所以這個時候的高水位線就在一千萬條數據這里。后來刪除這一千萬條數據的時候,由於delete語句不影響高水位線,所以高水位線依然在一千萬條數據這里。這個時候再一次用select語句進行掃描,雖然這個時候表中沒有數據,但是由於掃描是按照高水位線來的,所以需要把一千萬條數據的存儲空間都要掃描一次,也就是說這次掃描所需要的時間和掃描一千萬條數據所需要的時間是一樣多的。所以有時候有人總是經常說,怎么我的表中沒有幾條數據,但是還是這么慢呢,這個時候其實奧秘就是這里的高水位線了。
那有沒有辦法讓高水位線下降呢,其實有一種比較簡單的方法,那就是采用TRUNCATE語句進行刪除數據。采用TRUNCATE語句刪除一個表的數據的時候,類似於重新建立了表,不僅把數據都刪除了,還把HWM給清空恢復為0。所以如果需要把表清空,在有可能利用TRUNCATE語句來刪除數據的時候就利用TRUNCATE語句來刪除表,特別是那種數據量有可能很大的臨時存儲表。
在手動段空間管理(Manual Segment Space Management)中,段中只有一個HWM,但是在Oracle9iRelease1才添加的自動段空間管理(Automatic Segment Space Management)中,又有了一個低HWM的概念出來。為什么有了HWM還又有一個低HWM呢,這個是因為自動段空間管理的特性造成的。在手段段空間管理中,當數據插入以后,如果是插入到新的數據塊中,數據塊就會被自動格式化等待數據訪問。而在自動段空間管理中,數據插入到新的數據塊以后,數據塊並沒有被格式化,而是在第一次在第一次訪問這個數據塊的時候才格式化這個塊。所以我們又需要一條水位線,用來標示已經被格式化的塊。這條水位線就叫做低HWM。一般來說,低HWM肯定是低於等於HWM的。
五、修正ORACLE表的高水位線
在ORACLE中,執行對表的刪除操作不會降低該表的高水位線。而全表掃描將始終讀取一個段(extent)中所有低於高水位線標記的塊。如果在執行刪除操作后不降低高水位線標記,則將導致查詢語句的性能低下。下面的方法都可以降低高水位線標記。
1.執行表重建指令 alter table table_name move;
(在線轉移表空間ALTER TABLE 。。。 MOVE TABLESPACE 。。。ALTER TABLE 。。。 MOVE 后面不跟參數也行,不跟參數表還是在原來的表空間,move后記住重建索引。如果以后還要繼續向這個表增加數據,沒有必要move,只是釋放出來的空間,只能這個表用,其他的表或者segment無法使用該空間)
2.執行alter table table_name shrink space; 注意,此命令為Oracle 10g新增功能,再執行該指令之前必須允許行移動alter table table_name enable row movement;
3.復制要保留的數據到臨時表t,drop原表,然后rename臨時表t為原表
4.emp/imp
5.alter table table_name deallocate unused
6.盡量truncate吧
