高水位的介紹
數據庫運行了一段時間,經過一些列的刪除、插入、更改操作有些表的高水位線就有可能和實際的表存儲數據的情況相差特別多,為了提高檢索該表的效率,建議對這些表進行收縮;
查找表需要的存儲空間:表以數據塊的形式存儲在數據文件中,表的存儲結構是:行×行數,如果知道了總共有多少行,每行的平均長度,兩者相乘,再除於90%的使用率,那么就可以知道實際需要存儲的空間;
表的存儲結構;
從統計信息得出平均每行的長度和總共的行數,從而知道存儲的SIZE;
查找表實際存儲的空間:數據實際存儲在數據文件中是以塊的形式存儲的,每個數據文件8K,塊的數量乘於8k,就可以知道實際已經存儲的空間是多少了;
c) 查找數據庫中某個表空間下,可以實際存儲和需要的表空間差別最大的表,查找腳本如下:
SELECT NUM_ROWS,AVG_ROW_LEN*NUM_ROWS/1024/1024/0.9 NEED, BLOCKS*8/1024 TRUE,(BLOCKS*8/1024-AVG_ROW_LEN*NUM_ROWS/1024/1024/0.9) RECOVER_MB,TABLE_NAME
FROM dba_tables
WHERE tablespace_name='PSAPSR3' AND BLOCKS*8/1024-AVG_ROW_LEN*NUM_ROWS/1024/1024/0.9>100
desc;
因為所有的信息都是根據dba_tables,表的信息是根據統計信息得到的,所以如果統計信息不准確,那么整個搜索的結果都可能是錯誤的;
統計信息的腳本:exec dbms_stats.gather_table_stats('user','table_name');
a) 啟動行遷移:alter table table_name enable row movement ;
b)進行表的收縮:alter table table_name shrink space ;
SQL> select t.table_name,BLOCKS,EMPTY_BLOCKS,NUM_ROWS
from user_tables
where table_name = upper('table_name');
---------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------
一. 准備知識:Oracle的邏輯存儲管理.
ORACLE在邏輯存儲上分4個粒度: 表空間, 段, 區 和 塊.
1.1 塊: 是粒度最小的存儲單位,現在標准的塊大小是8K,ORACLE每一次I/O操作也是按塊來操作的,也就是說當ORACLE從數據文件讀數據時,是讀取多少個塊,而不是多少行. 每一個Block里可以包含多個row.
1.2 區: 由一系列相鄰的塊而組成,這也是ORACLE空間分配的基本單位,舉個例子來說,當我們創建一個表Dave時,首先ORACLE會分配一區的空間給這個表,隨着不斷的INSERT數據到Dave,原來的這個區容不下插入的數據時,ORACLE是以區為單位進行擴展的,也就是說再分配多少個區給Dave,而不是多少個塊.
1.3 段: 是由一系列的區所組成, 一般來說, 當創建一個對象時(表,索引),就會分配一個段給這個對象. 所以從某種意義上來說,段就是某種特定的數據.如CREATE TABLE Dave,這個段就是數據段,而CREATE INDEX ON Dave(NAME), ORACLE同樣會分配一個段給這個索引,但這是一個索引段了.查詢段的信息可以通過數據字典: SELECT * FROM USER_SEGMENTS來獲得.
1.4 表空間: 包含段,區及塊.表空間的數據物理上儲存在其所在的數據文件中.一個數據庫至少要有一個表空間.
表空間(tableSpace) 段(segment) 盤區(extent) 塊(block) 關系
http://blog.csdn.net/tianlesoftware/archive/2009/12/08/4962476.aspx
當我們創建了一個表,即使我沒有插入任何一行記錄, ORACLE還是給它分配了8個塊. 當然這個跟建表語句的INITIAL 參數及MINEXTENTS參數有關. 如:
STORAGE
(
INITIAL 64K
MINEXTENTS 1
MAXEXTENTS UNLIMITED
);
也就是說,在這個對象創建以后,ORACLE至少給它分配一個區,初始大小是64K,一個標准塊的大小是8K,剛好是8個BLOCK.
Oracle Table 創建參數 說明
http://blog.csdn.net/tianlesoftware/archive/2009/12/07/4954417.aspx
二. 高水線(High Water Mark)
2.1 官網說明如下
http://download.oracle.com/docs/cd/E11882_01/server.112/e16508/logical.htm#CNCPT89022
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 all blocks 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.
Oracle 自動段空間管理(ASSM:auto segment space management)
http://blog.csdn.net/tianlesoftware/archive/2009/12/07/4958989.aspx
Every data block in an ASSM segment is in one of the following states:
(1)Above the HWM
These blocks are unformatted and have never been used.
(2)Below the HWM
These blocks are in one of the following states:
(1)Allocated, but currently unformatted and unused
(2)Formatted and contain data
(3)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

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.
Figure 12-26 Advancing HWM and Low HWM

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.
Oracle 數據塊 Block 說明
http://blog.csdn.net/tianlesoftware/archive/2011/05/12/6414765.aspx
2. 2 Oracle表段中的高水位線HWM
在Oracle數據的存儲中,可以把存儲空間想象為一個水庫,數據想象為水庫中的水。水庫中的水的位置有一條線叫做水位線,在Oracle中,這條線被稱為高水位線(High-warter mark, HWM)。在數據庫表剛建立的時候,由於沒有任何數據,所以這個時候水位線是空的,也就是說HWM為最低值。當插入了數據以后,高水位線就會上漲,但是這里也有一個特性,就是如果你采用delete語句刪除數據的話,數據雖然被刪除了,但是高水位線卻沒有降低,還是你剛才刪除數據以前那么高的水位。也就是說,這條高水位線在日常的增刪操作中只會上漲,不會下跌。HWM通常增長的幅度為一次5個數據塊.
Select語句會對表中的數據進行一次掃描,但是究竟掃描多少數據存儲塊呢,這個並不是說數據庫中有多少數據,Oracle就掃描這么大的數據塊,而是Oracle會掃描高水位線以下的數據塊。現在來想象一下,如果剛才是一張剛剛建立的空表,你進行了一次Select操作,那么由於高水位線HWM在最低的0位置上,所以沒有數據塊需要被掃描,掃描時間會極短。而如果這個時候你首先插入了一千萬條數據,然后再用delete語句刪除這一千萬條數據。由於插入了一千萬條數據,所以這個時候的高水位線就在一千萬條數據這里。后來刪除這一千萬條數據的時候,由於delete語句不影響高水位線,所以高水位線依然在一千萬條數據這里。這個時候再一次用select語句進行掃描,雖然這個時候表中沒有數據,但是由於掃描是按照高水位線來的,所以需要把一千萬條數據的存儲空間都要掃描一次,也就是說這次掃描所需要的時間和掃描一千萬條數據所需要的時間是一樣多的。所以有時候有人總是經常說,怎么我的表中沒有幾條數據,但是還是這么慢呢,這個時候其實奧秘就是這里的高水位線了。
那有沒有辦法讓高水位線下降呢,其實有一種比較簡單的方法,那就是采用TRUNCATE語句進行刪除數據。采用TRUNCATE語句刪除一個表的數據的時候,類似於重新建立了表,不僅把數據都刪除了,還把HWM給清空恢復為0。所以如果需要把表清空,在有可能利用TRUNCATE語句來刪除數據的時候就利用TRUNCATE語句來刪除表,特別是那種數據量有可能很大的臨時存儲表。
在手動段空間管理(Manual Segment Space Management)中,段中只有一個HWM,但是在Oracle 9i Release1才添加的自動段空間管理(Automatic Segment Space Management)中,又有了一個低HWM的概念出來。為什么有了HWM還又有一個低HWM呢,這個是因為自動段空間管理的特性造成的。在手段段空間管理中,當數據插入以后,如果是插入到新的數據塊中,數據塊就會被自動格式化等待數據訪問。而在自動段空間管理中,數據插入到新的數據塊以后,數據塊並沒有被格式化,而是在第一次訪問這個數據塊的時候才格式化這個塊。所以我們又需要一條水位線,用來標示已經被格式化的塊。這條水位線就叫做低HWM。一般來說,低HWM肯定是低於等於HWM的。
2.3. 修正ORACLE表的高水位線
在ORACLE中,執行對表的刪除操作不會降低該表的高水位線。而全表掃描將始終讀取一個段(extent)中所有低於高水位線標記的塊。如果在執行刪除操作后不降低高水位線標記,則將導致查詢語句的性能低下。rebuild, truncate, shrink,move 等操作會降低高水位。
2.3.1 執行表重建指令 alter table table_name move;
在線轉移表空間ALTER TABLE ... MOVE TABLESPACE ..
當你創建了一個對象如表以后,不管你有沒有插入數據,它都會占用一些塊,ORACLE也會給它分配必要的空間.同樣,用ALTER TABLE MOVE釋放自由空間后,還是保留了一些空間給這個表.
ALTER TABLE ... MOVE 后面不跟參數也行,不跟參數表還是在原來的表空間,Move后記住重建索引. 如果以后還要繼續向這個表增加數據,沒有必要move,只是釋放出來的空間,只能這個表用,其他的表或者segment無法使用該空間。
2.3.2 執行alter table table_name shrink space;
此命令為Oracle 10g新增功能,再執行該指令之前必須允許行移動 alter table table_name enable row movement;
2.3.3 重建表
復制要保留的數據到臨時表t,drop原表,然后rename臨時表t為原表
2.3.4 用邏輯導入導出: Emp/Imp
2.3.5. Alter table table_name deallocate unused
DEALLOCATE UNUSED為釋放HWM上面的未使用空間,但是並不會釋放HWM下面的自由空間,也不會移動HWM的位置.
2.3.6 推薦使用truncate.
2.3.7 一些注意事項
Oracle 9i:
(1)如果是INEXTENT, 可以使alter table tablename deallocate unused將HWM以上所有沒使用的空間釋放
(2) 如果MINEXTENT >HWM 則釋放MINEXTENTS 以上的空間。如果要釋放HWM以上的空間則使用KEEP 0。
SQL>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
2.4 HWM 特點
2.4.1 ORACLE用HWM來界定一個段中使用的塊和未使用的塊.
舉個例子來說,當我們創建一個表時,ORACLE就會為這個對象分配一個段.在這個段中,即使我們未插入任何記錄,也至少有一個區被分配,第一個區的第一個塊就稱為段頭(SEGMENT HEADE),段頭中就儲存了一些信息,基中HWM的信息就存儲在此.
此時,因為第一個區的第一塊用於存儲段頭的一些信息,雖然沒有存儲任何實際的記錄,但也算是被使用,此時HWM是位於第2個塊.當我們不斷插入數據到表后,第1個塊已經放不下后面新插入的數據,此時,ORACLE將高水位之上的塊用於存儲新增數據,同時,HWM本身也向上移.也就是說,當我們不斷插入數據時,HWM會往不斷上移,這樣,在HWM之下的,就表示使用過的塊,HWM之上的就表示已分配但從未使用過的塊.
2.4.2. HWM在插入數據時,當現有空間不足而進行空間的擴展時會向上移,但刪除數據時不會往下移.
ORACLE 不會釋放空間以供其他對象使用,有一條簡單的理由:由於空間是為新插入的行保留的,並且要適應現有行的增長。被占用的最高空間稱為最高使用標記 (HWM),
2.4.3. HWM的信息存儲在段頭當中.
HWM本身的信息是儲存在段頭.在段空間是手工管理方式時,ORACLE是通過FREELIST(一個單向鏈表)來管理段內的空間分配.在段空間是自動管理方式時(ASSM),ORACLE是通過BITMAP來管理段內的空間分配.
2.4.4. ORACLE的全表掃描是讀取高水位標記(HWM)以下的所有塊.
所以問題就產生了.當用戶發出一個全表掃描時,ORACLE 始終必須從段一直掃描到 HWM,即使它什么也沒有發現。該任務延長了全表掃描的時間。
2.4.5. 當用直接路徑插入行時,即使HWM以下有空閑的數據庫塊,鍵入在插入數據時使用了append關鍵字,則在插入時使用HWM以上的數據塊,此時HWM會自動增大。
例如,通過直接加載插入(用 APPEND 提示插入)或通過 SQL*LOADER 直接路徑 數據塊直接置於 HWM 之上。它下面的空間就浪費掉了。
三. 相關測試
1) 創建測試表
SQL> create table tt (id number);
Table created.
此時表沒有分析,是原始的數據,即8個數據塊。
SQL>SELECT segment_name,segment_type,blocks FROM dba_segments WHERE segment_name='TT';
SEGMENT_NAME SEGMENT_TYPE BLOCKS
--------------- --------------- ----------
TT TABLE 8
SQL> SELECT table_name,num_rows,blocks,empty_blocks FROM user_tables WHERE table_name='TT';
TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS
--------------- ---------- ---------- ------------
TT
2) 向表中插入一些測試數據
SQL> declare
2 i number;
3 begin
4 for i in 1..10000 loop
5 insert into tt values(i);
6 end loop;
7 commit;
8 end;
9 /
PL/SQL procedure successfully completed.
3)在次查看表的信息
SQL> SELECT table_name,num_rows,blocks,empty_blocks FROM user_tables WHERE table_name='TT';
TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS
--------------- ---------- ---------- ------------
TT
SQL> SELECT segment_name,segment_type,blocks FROM dba_segments WHERE segment_name='TT';
SEGMENT_NAME SEGMENT_TYPE BLOCKS
--------------- --------------- ----------
TT TABLE 24
此時表TT 占用的數據庫已經是24個了。 但是user_tables 顯示的信息還是為空。 因為沒有做統計分析。
4) 收集統計信息
SQL> exec DBMS_STATS.GATHER_TABLE_STATS('SYS','TT');
PL/SQL procedure successfully completed.
SQL> SELECT segment_name,segment_type,blocks FROM dba_segments WHERE segment_name='TT';
SEGMENT_NAME SEGMENT_TYPE BLOCKS
--------------- --------------- ----------
TT TABLE 24
SQL> SELECT table_name,num_rows,blocks,empty_blocks FROM user_tables WHERE table_name='TT';
TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS
--------------- ---------- ---------- ------------
TT 10000 20 0
此時user_tables 已經有了數據,顯示的使用了20個數據塊。 但是empty_blocks 還是為空。 這里要注意的地方。 這個字段只有使用analyze 收集統計信息之后才會有數據。
5) 使用analyze 收集統計信息
SQL> ANALYZE TABLE TT COMPUTE STATISTICS;
Table analyzed.
SQL> SELECT table_name,num_rows,blocks,empty_blocks FROM user_tables WHERE table_name='TT';
TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS
--------------- ---------- ---------- ------------
TT 10000 20 3
-- 這里有顯示空的數據庫有3個。 注意:20+3=23. 比占用的24個數據塊少一個。因為有一個數據庫塊被保留用作segment header。
SQL> SELECT segment_name,segment_type,blocks FROM dba_segments WHERE segment_name='TT';
SEGMENT_NAME SEGMENT_TYPE BLOCKS
--------------- --------------- ----------
TT TABLE 24
6) delete 數據,不會降低高水位
SQL> delete from tt;
10000 rows deleted.
SQL> commit;
Commit complete.
SQL> SELECT segment_name,segment_type,blocks FROM dba_segments WHERE segment_name='TT';
SEGMENT_NAME SEGMENT_TYPE BLOCKS
--------------- --------------- ----------
TT TABLE 24
SQL> SELECT table_name,num_rows,blocks,empty_blocks FROM user_tables WHERE table_name='TT';
TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS
--------------- ---------- ---------- ------------
TT 10000 20 3
SQL> analyze table tt compute statistics;
Table analyzed.
SQL> SELECT table_name,num_rows,blocks,empty_blocks FROM user_tables WHERE table_name='TT';
TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS
--------------- ---------- ---------- ------------
TT 0 20 3
SQL> SELECT segment_name,segment_type,blocks FROM dba_segments WHERE segment_name='TT';
SEGMENT_NAME SEGMENT_TYPE BLOCKS
--------------- --------------- ----------
TT TABLE 24
SQL>
7) truncate 表,可以降低高水位
SQL> truncate table tt;
Table truncated.
SQL> SELECT segment_name,segment_type,blocks FROM dba_segments WHERE segment_name='TT';
SEGMENT_NAME SEGMENT_TYPE BLOCKS
--------------- --------------- ----------
TT TABLE 8
SQL> SELECT table_name,num_rows,blocks,empty_blocks FROM user_tables WHERE table_name='TT';
TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS
--------------- ---------- ---------- ------------
TT 0 20 3
-- 段的信息沒有改變,收集一下統計信息看看
SQL> exec dbms_stats.gather_table_stats('SYS','TT');
PL/SQL procedure successfully completed.
SQL> SELECT table_name,num_rows,blocks,empty_blocks FROM user_tables WHERE table_name='TT';
TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS
--------------- ---------- ---------- ------------
TT 0 0 3
SQL> SELECT segment_name,segment_type,blocks FROM dba_segments WHERE segment_name='TT';
SEGMENT_NAME SEGMENT_TYPE BLOCKS
--------------- --------------- ----------
TT TABLE 8
--段的信息已經改變,但是empty_blocks 段沒有改變,該段只有使用analyze 才能改變。
SQL> analyze table tt compute statistics;
Table analyzed.
SQL> SELECT table_name,num_rows,blocks,empty_blocks FROM user_tables WHERE table_name='TT';
TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS
--------------- ---------- ---------- ------------
TT 0 0 7
SQL> SELECT segment_name,segment_type,blocks FROM dba_segments WHERE segment_name='TT';
SEGMENT_NAME SEGMENT_TYPE BLOCKS
--------------- --------------- ----------
TT TABLE 8
SQL>
-- 總共8個數據塊,7個為空,還有一個是segment header。
四. Alter table move 和Shrink 區別
在下面2篇blog 有說明:
http://blog.csdn.net/robinson1988/archive/2010/09/07/5868742.aspx
alter table move跟shrink space的區別
http://blog.csdn.net/wyzxg/archive/2010/05/28/5631721.aspx
MOS 上的說明,ID:577375.1:
The shrink algorithm starts from the bottom of the segment and starts moving those rows to the beginning of the segment. Shrink is a combination of delete/insert pair for every row movement and this generates many UNDO and REDO blocks .
Move從segment的底部開始,move這些rows到segment的頭部。Shrink則是delete/insert相結合,這樣會產生非常多的UNDO和REDO。
4.1 Shrink
在10g之后,整理碎片消除行遷移的新增功能shrink space
SQL>alter table <table_name> shrink space [ <null> | compact | cascade ];
compact: 這個參數當系統的負載比較大時可以用,不降低HWM。如果系統負載較低時,直接用alter table table_name shrink space就一步到位了
cascade:這個參數是在shrink table的時候自動級聯索引,相當於rebulid index。
以下SQL 基於普通表
shrink必須開啟行遷移功能。
alter table table_name enable row movement ;
保持HWM,相當於把塊中數據打結實了
alter table table_name shrink space compact;
回縮表與降低HWM
alter table table_name shrink space;
回縮表與相關索引,降低HWM
alter table table_name shrink space cascade;
回縮索引與降低HWM
alter index index_name shrink space
雖然在10g中可以用shrink ,但也有些限制:
1). 對cluster,cluster table,或具有Long,lob類型列的對象 不起作用。
2). 不支持具有function-based indexes 或 bitmap join indexes的表
3). 不支持mapping 表或index-organized表。
4). 不支持compressed 表
4.2 Move
通過desc table_name 來檢查表中是否有LOB 字段, 如果表沒有LOB字段, 直接 alter table move; 然后 rebuild index
如果表中包含了LOB字段,如用如下SQL:
SQL>alter table owner.table_name move tablespace tablespace_name lob (lob_column) store as lobsegment tablespace tablespace_name;
也可以單獨move lob,但是表上的index 同樣會失效. 所以在操作結束,需要對索引進行rebuild。
SQL>alter table owner.table_name move lob(lob_column) store as lobsegment tablespace tablespace_name ;
索引的rebuild:
首先用下面的SQL查看表上面有哪類索引:
SELECT a.owner,
a.index_name,
a.index_type,
a.partitioned,
a.status,
b.status p_status,
b.composite
FROM dba_indexes a
LEFT JOIN
dba_ind_partitions b
ON a.owner = b.index_owner AND a.index_name = b.index_name
WHERE a.owner = '&owner' AND a.table_name = '&table_name';
對於普通索引直接rebuild online nologging parallel,
對於分區索引,必須單獨rebuild 每個分區,
對於組合分區索引,必須單獨rebuild 每個子分區。
Move 通過移動數據來來降低HWM,因此需要更多的磁盤空間。 Shrink 通過delete 和 insert, 會產生較多的undo 和redo。
shrink space收縮到數據存儲的最小值,alter table move(不帶參數)收縮到initial指定值,也可以用alter table test move storage(initial 500k)指定收縮的大小,這樣可以達到shrink space效果。
總之,使用Move 效率會高點,但是會導致索引失效。Shrink 會產生undo 和redo,速度相對也慢一點。
