高水位(High Water Mark)的概念及高水位問題的解決

說到HWM,我們首先要簡要的談談ORACLE的邏輯存儲管理.我們知道,ORACLE在邏輯存儲上分4個粒度:表空間,段,區和塊.
 
(1)塊:是粒度最小的存儲單位,現在標準的塊大小是8K,ORACLE每一次I/O操作也是按塊來操作的,也就是說當ORACLE從數據文件讀數據時,是讀取多少個塊,而不是多少行.
 
(2) 區:由一系列相鄰的塊而組成,這也是ORACLE空間分配的基本單位,舉個例子來說,當我們創建一個表PM_USER時,首先ORACLE會分配一區的空 間給這個表,隨着不斷的INSERT數據到PM_USER,原來的這個區容不下插入的數據時,ORACLE是以區爲單位進行擴展的,也就是說再分配多少個 區給PM_USER,而不是多少個塊. 
 
(3) 段:是由一系列的區所組成,一般來說,當創建一個對象時(表,索引),就會分配一個段給這個對象.所以從某種意義上來說,段就是某種特定的數據.如 CREATE TABLE PM_USER,這個段就是數據段,而CREATE INDEX ON PM_USER(NAME),ORACLE同樣會分配一個段給這個索引,但這是一個索引段了.查詢段的信息可以通過數據字典: SELECT * FROM USER_SEGMENTS來獲得,
 
(4)表空間:包含段,區及塊.表空間的數據物理上儲存在其所在的數據文件中.一個數據庫至少要有一個表空間.
 
OK,我們現在回到HWM上來,那麼,什麼是高水位標記呢?這就跟ORACLE的段空間管理相關了.
 
 
(一)ORACLE用HWM來界定一個段中使用的塊和未使用的塊.


舉 個例子來說,當我們創建一個表:PT_SCHE_DETAIL時,ORACLE就會爲這個對象分配一個段.在這個段中,即使我們未插入任何記錄,也至少有 一個區被分配,第一個區的第一個塊就稱爲段頭(SEGMENT HEADE),段頭中就儲存了一些信息,基中HWM的信息就存儲在此.此時,因爲第一個區的第一塊用於存儲段頭的一些信息,雖然沒有存儲任何實際的記錄, 但也算是被使用,此時HWM是位於第2個塊.當我們不斷插入數據到PM_USER後,第1個塊已經放不下後面新插入的數據,此時,ORACLE將高水位之 上的塊用於存儲新增數據,同時,HWM本身也向上移.也就是說,當我們不斷插入數據時,HWM會往不斷上移,這樣,在HWM之下的,就表示使用過的 塊,HWM之上的就表示已分配但從未使用過的塊.
 
(二)HWM在插入數據時,當現有空間不足而進行空間的擴展時會向上移,但刪除數據時不會往下移.
 
這就好比是水庫的水位,當漲水時,水位往上移,當水退出後,最高水位的痕跡還是清淅可見.
考慮讓我們看一個段,如一張表,其中填滿了塊,如圖 1 所示。在正常操作過程中,刪除了一些行,如圖 2 所示。現有就有了許多浪費的空間:(I) 在表的上一個末端和現有的塊之間,以及 (II) 在塊內部,其中還有一些沒有刪除的行。
 

圖1:分配給該表的塊。用灰色正方形表示行
 
ORACLE 不會釋放空間以供其他對象使用,有一條簡單的理由:由於空間是爲新插入的行保留的,並且要適應現有行的增長。被佔用的最高空間稱爲最高使用標記 (HWM),如圖 2 所示。
 

圖2:行後面的塊已經刪除了;HWM 仍保持不變
 
(三)HWM的信息存儲在段頭當中.


HWM本身的信息是儲存在段頭.在段空間是手工管理方式時,ORACLE是通過FREELIST(一個單向鏈表)來管理段內的空間分配.在段空間是自動管理方式時(ASSM),ORACLE是通過BITMAP來管理段內的空間分配.
 
(四)ORACLE的全表掃描是讀取高水位標記(HWM)以下的所有塊.
 
所以問題就產生了.當用戶發出一個全表掃描時,ORACLE 始終必須從段一直掃描到 HWM,即使它什麼也沒有發現。該任務延長了全表掃描的時間。
 
(五)當用直接路徑插入行時 — 例如,通過直接加載插入(用 APPEND 提示插入)或通過 SQL*LOADER 直接路徑 — 數據塊直接置於 HWM 之上。它下面的空間就浪費掉了。
 
我們來分析這兩個問題,後者只是帶來空間的浪費,但前者不僅是空間的浪費,而且會帶來嚴重的性能問題.我們來看看下面的例子:
 
(A)我們先來搭建測試的環境,第一步先創建一個段空間爲手工管理的表空間:
 
CREATE TABLESPACE "RAINNY"
LOGGING
DATAFILE 'D:ORACLE_HOMEORADATARAINNYRAINNY.ORA' SIZE 5M
AUTOEXTEND
ON NEXT 10M MAXSIZE UNLIMITED EXTENT MANAGEMENT LOCAL
SEGMENT SPACE MANAGEMENT MANUAL;
 
(B)創建一個表,注意,此表的第二個字段我故意設成是CHAR(100),以讓此表在插入1千萬條記錄後,空間有足夠大:
 
CREATE TABLE TEST_TAB(C1 NUMBER(10),C2 CHAR(100)) TABLESPACE RAINNY;
 
插入記錄
 
DECLARE
    I NUMBER(10);
BEGIN
    FOR I IN 1..10000000 LOOP
        INSERT INTO TEST_TAB VALUES(I,'TESTSTRING');
    END LOOP;
    COMMIT;
END;
/

(C)我們來查詢一下,看在插入一千萬條記錄後所訪問的塊數和查詢所用時間:

SQL> SET TIMING ON
SQL> SET AUTOTRACE TRACEONLY
SQL> SELECT COUNT(*) FROM TEST_TAB;
 
ELAPSED: 00:01:03.05
 
EXECUTION PLAN
------------------------------------------------------------
0 SELECT STATEMENT OPTIMIZER=CHOOSE (COST=15056 CARD=1)
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (FULL) OF 'TEST_TAB' (COST=15056 CARD=10000
000)
 
STATISTICS
----------------------------------------------------------
0      RECURSIVE CALLS
0      DB BLOCK GETS
156310 CONSISTENT GETS
154239 PHYSICAL READS
0      REDO SIZE
379    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

我們來看上面的執行計劃,這句SQL總供耗時是:1分3秒.訪問方式是採用全表掃描方式(FTS),邏輯讀了156310個BLOCK,物理讀了154239個BLOCK.
我們來分析一下這個表:
 
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(OWNNAME=> 'TEST',
TABNAME=> 'TEST_TAB',
PARTNAME=> NULL);END;
/

發現這個表目前使用的BLOCK有: 156532,未使用的BLOCK(EMPTY_BLOCKS)爲:0,總行數爲(NUM_ROWS):1000 0000
 
(D)接下來我們把此表的記錄用DELETE方式刪掉,然後再來看看SELECT COUNT(*) FROM TEST_TAB所花的時間:
 
DELETE FROM TEST_TAB;
COMMIT;
 
SQL> SELECT COUNT(*) FROM TEST_TAB;
 
ELAPSED: 00:01:04.03
 
EXECUTION PLAN
----------------------------------------------------------
0 SELECT STATEMENT OPTIMIZER=CHOOSE (COST=15056 CARD=1)
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (FULL) OF 'TEST_TAB' (COST=15056 CARD=1)
 
STATISTICS
----------------------------------------------------------
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表後,此時表中已沒有一條記錄,爲什麼SELECT COUNT(*) FROM TEST_TAB花的時間爲1分4秒, 反而比有記錄稍微長點,這是爲什麼呢?而且大家看,其邏輯讀了156310個 BLOCK,跟之前有一千萬行記錄時差不多,ORACLE怎麼會這麼笨啊?


我們在DELETE表後再次分析表,看看有什麼變化:
這時, TEST_TAB表目前使用的BLOCK是: 156532,未使用的BLOCK(EMPTY_BLOCKS)爲:0,總行數爲(NUM_ROWS)已變成:0
爲什麼表目前使的BLOCK數還是156532呢?

問 題的根源就在於ORACLE的HWM.也就是說,在新增記錄時,HWM會慢慢往上移,但是在刪除記錄後,HWM卻不會往下移,也就是說,DELETE一千 萬條記錄後,此表的HWM根本沒移動,還在原來的那個位置,所以,HWM以下的塊數同樣也是一樣的.ORACLE的全表掃描是讀取ORACLE高水位標記 下的所有BLOCK,也就是說,不管HWM下的BLOCK現在實際有沒有存放數據,ORACLE都會一一讀取,這樣,大家可想而知,在我們DELETE表 後,ORACLE讀了大量的空塊,耗去了大量的時間.
 

我們再來看DELETE表後段空間實際使用的狀況:
 
SQL> EXEC SHOW_SPACE('TEST_TAB','TEST');
TOTAL BLOCKS............................164352 --總共164352塊
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塊
 
PL/SQL PROCEDURE SUCCESSFULLY COMPLETED
 
總 共用了164352塊,除了一個SEGMENT HEADER,實際總共用了164351個塊,有7168塊從來沒有使用過。LAST USED BLOCK表示在最後一個使用的EXTENT 中使用的BLOCK, 結合 LAST USED EXT BLOCK ID可以計算 HWM 位置 :

LAST USED EXT BLOCK ID + LAST USED BLOCK -1 = HWM 所在的數據文件的BLOCK編號
 
代入得出: 158856+1024-1=159879,這個就是HWM所有的BLOCK編號
HWM所在的塊:TOTAL BLOCKS- UNUSED BLOCKS=164352-7168=157184,也就是說,HWM在第157184個塊,其BLOCKID是159879
 
(E)結下來,我們再做幾個試驗:
 
第一步:執行ALTER TABLE TEST_TAB DEALLOCATE UNUSED;
我們看看段空間的使用狀況:
 
SQL> EXEC SHOW_SPACE('TEST_TAB','TEST');
 
TOTAL BLOCKS............................157184
TOTAL BYTES.............................1287651328
UNUSED BLOCKS...........................0
UNUSED BYTES............................0
LAST USED EXT FILEID....................9
LAST USED EXT BLOCKID...................158856
LAST USED BLOCK.........................1024
 
此 時我們再代入上面的公式,算出HWM的位置: 157184-0=157184 HWM所在的BLOCK ID是158856+1024-1=159879,跟剛剛的沒有變化,也就是說執行ALTER TABLE TEST_TAB DEALLOCATE UNUSED後,段的高水位標記的位置沒有改變,但是大家看看UNUSED BLOCKS變爲0了,總的塊數減少到157184,這證明,DEALLOCATE UNUSED爲釋放HWM上面的未使用空間,但是並不會釋放HWM下面的自由空間,也不會移動HWM的位置.
 
第二步:我們再來看看執行ALTER TABLE TEST_TAB MOVE後段空間的使用狀況:
 
SQL> EXEC SHOW_SPACE('TEST_TAB','TEST');
 
TOTAL BLOCKS............................8
TOTAL BYTES.............................65536
UNUSED BLOCKS...........................5
UNUSED BYTES............................40960
LAST USED EXT FILEID....................9
LAST USED EXT BLOCKID...................2632
LAST USED BLOCK.........................3

此時,總共用到的塊數已變爲8, 我們再代入上面的公式,算出HWM的位置: 8-5=3 HWM所在的BLOCK ID是2632+3-1=2634,
 
OK, 我們發現,此時HWM的位置已經發生變化,現在HWM的位置是在第3個BLOCK,其BLOCK ID是2634,所有數據文件的ID是9(這個沒有發生變化,數據文件還是原來的那個數據文件,只是釋放了原來的自由空間),最後使用的塊數也變爲3,也 就是說已經使用了3塊,HWM就是在最後一個使用的塊上,即第3個塊上.大家可能會覺得奇怪,爲什麼釋放空間後,未使用的塊還有5個啊?也就是說HWM之 上還是有5個已分配但從未使用的塊.答案就跟HWM移動的規律有關.當我們在插入數據時,ORACLE首先在HWM之下的塊當中定位自由空間(通過自由列 表FREELIST),如果FREELIST當中沒有自由塊了,ORACLE就開始往上擴展,而HWM也跟着往上移,每5塊移動一次.我們來看 ORACLE的說明:
 
The high water mark is:
-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…
 
我們再來看看:SELECT COUNT(*) FROM TEST_TAB所花的時間:
 
SQL> SELECT COUNT(*) FROM TEST_TAB;
 
ELAPSED: 00:00:00.00
EXECUTION PLAN
----------------------------------------------------------
0 SELECT STATEMENT OPTIMIZER=CHOOSE
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (FULL) OF 'TEST_TAB'
 
STATISTICS
----------------------------------------------------------
0     RECURSIVE CALLS
0     DB BLOCK GETS
3     CONSISTENT 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
 
很快,不到1秒.
 
我們最後再來對錶作一次分析, 此時這個表目前使用的BLOCK爲: 0,未使用的BLOCK(EMPTY_BLOCKS)爲:0,總行數爲(NUM_ROWS):0
從 中我們也可以發現,分析表和SHOW_SPACE顯示的數據有點不一致.那麼哪個是準的呢?其實這兩個都是準的,只不過計算的方法有點不同.事實上,當你 創建了一個對象如表以後,不管你有沒有插入數據,它都會佔用一些塊,ORACLE也會給它分配必要的空間.同樣,用ALTER TABLE MOVE釋放自由空間後,還是保留了一些空間給這個表.
最後,我們再來執行TRUNCATE命令,截斷這個表,看看段空間的使用狀況:
 
TRUNCATE TABLE TEST_TAB;
 
SQL> EXEC SHOW_SPACE('TEST_TAB','TEST');
 
TOTAL BLOCKS............................8
TOTAL BYTES.............................65536
UNUSED BLOCKS...........................5
UNUSED BYTES............................40960
LAST USED EXT FILEID....................9
LAST USED EXT BLOCKID...................2632
LAST USED BLOCK.........................3
 
PL/SQL PROCEDURE SUCCESSFULLY COMPLETED
 
SQL>
 
我們發現TRUNCATE後和MOVE沒有什麼變化.
 
爲了,最終驗證一下我上面的觀點,我再DROP一下表,然後新建這個表,看看這時在沒有插入任何數據之前,是否ORACLE確實有給這個對象分配必要的空間:
 
DROP TABLE TEST_TAB;
 
CREATE TABLE TEST_TAB(C1 NUMBER(10),C2 CHAR(100)) TABLESPACE RAINNY;
 
SQL> EXEC SHOW_SPACE('TEST_TAB','TEST');
TOTAL BLOCKS............................8
TOTAL BYTES.............................65536
UNUSED BLOCKS...........................5
UNUSED BYTES............................40960
LAST USED EXT FILEID....................9
LAST USED EXT BLOCKID...................2112
LAST USED BLOCK.........................3
 
大家看,即使我沒有插入任何一行記錄,ORACLE還是給它分配了8個塊.當然這個跟建表語句的INITIAL 參數及MINEXTENTS參數有關:請看TEST_TAB的存儲參數:
 
S TORAGE
(
INITIAL 64K
MINEXTENTS 1
MAXEXTENTS UNLIMITED
);
 
也就是說,在這個對象創建以後,ORACLE至少給它分配一個區,初始大小是64K,一個標準塊的大小是8K,剛好是8個BLOCK.
 
 
總結:
 
在9I中:
 
(1)如果MINEXTENT 可以使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:
 
可以使用ALTER TABLE TEST_TAB SHRINK SPACE命令來聯機移動HWM,
如果要同時壓縮表的索引,可以發佈:ALTER TABLE TEST_TAB SHRINK SPACE CASCADE
注意:在使用此命令時需要先使行可遷移row movement(具體見例子)。
         與使用ALTER TABLE MOVE 不同的是執行此命令後並不需要重構索引。
 
 
Oracle 官方說明
 
Shrinking Database Segments Online
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
 
 
操作的過程:
 
SQL> create table demo as select * from dba_source;
Table created.
Elapsed: 00:00:05.83
 
SQL> select count(*) from demo;
 
  COUNT(*)
----------
    210992
Elapsed: 00:00:01.06
 
SQL> insert into demo select * from demo;
210992 rows created.
Elapsed: 00:00:59.83
 
SQL> commit;
Commit complete.
 
//得到一個40萬條記錄的表,下面來查看這個表空間分佈情況。
 
SQL> exec show_space('demo','auto');
 
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.07
 
SQL> set serveroutput on
 
SQL>  exec show_space('demo','auto');
 
Total Blocks............................9216
Total Bytes.............................75497472
Unused Blocks...........................768
Unused Bytes............................6291456
Last Used Ext FileId....................4
Last Used Ext BlockId...................8328
Last Used Block.........................256
 
一共有9216個數據塊,HWM在9216-768=8448這個塊.
也可以通過查看extents得到HWM=8*16+128*63+256=8192+256=8448
 
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.01
 
SQL> delete from demo where rownum<220000;
 
219999 rows deleted.
Elapsed: 00:00:40.99
 
SQL> commit;
 
Commit complete.
Elapsed: 00:00:00.01
 
SQL>  exec show_space('demo','auto');
 
Total Blocks............................9216
Total Bytes.............................75497472
Unused Blocks...........................768
Unused Bytes............................6291456
Last Used Ext FileId....................4
Last Used Ext BlockId...................8328
Last Used Block.........................256
 
PL/SQL procedure successfully completed.
 
//刪除操作後表的HWM沒有變化,還是在第8448塊這個位置。
Elapsed: 00:00:00.00
 
SQL> alter table demo shrink space;
alter table demo shrink space
*
ERROR at line 1:
ORA-10636: ROW MOVEMENT is not enabled
//先要enable row movement才能shrink
Elapsed: 00:00:00.09
 
SQL> alter table demo enable row movement;
 
Table altered.
Elapsed: 00:00:00.10
 
SQL>  alter table demo shrink space;
 
Table altered.
Elapsed: 00:01:35.51
 
SQL>  exec show_space('demo','auto');
 
Total Blocks............................3656
Total Bytes.............................29949952
Unused Blocks...........................0
Unused Bytes............................0
Last Used Ext FileId....................4
Last Used Ext BlockId...................3720
Last Used Block.........................72
 
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.02
//可以看到HWM降到了3656這個塊上面!



---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
最近學習了一陣子Oracle, 感覺Oracle真的是博大精深, 包括Oralce內存結構,性能調整,數據備份等都不簡單, 這些對開發也很重要, 下面把做的Oracle高水位線的一些實驗貼出來, 方便以後Review:
高水位線實驗:
-- 創建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 會自動增大。

 



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章