舉 個例子來說,當我們創建一個表: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 會自動增大。