高水位(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操作后,則最好回收一下表的高水位。