1. 準備知識:ORACLE的邏輯存儲管理.
ORACLE在邏輯存儲上分4個粒度:表空間,段,區和塊.
1.1 塊:是粒度最小的存儲單位,現在標準的塊大小是8K,ORACLE每一次I/O操作也是按塊來操作的,也就是說當ORACLE從數據文件讀數據時,是讀取多少個塊,而不是多少行.
1.2 區:由一系列相鄰的塊而組成,這也是ORACLE空間分配的基本單位,舉個例子來說,當我們創建一個表PM_USER時,首先ORACLE會分配一區的空間給這個表,隨着不斷的INSERT數據到PM_USER,原來的這個區容不下插入的數據時,ORACLE是以區爲單位進行擴展的,也就是說再分配多少個區給PM_USER,而不是多少個塊.
1.3 段:是由一系列的區所組成,一般來說,當創建一個對象時(表,索引),就會分配一個段給這個對象.所以從某種意義上來說,段就是某種特定的數據.如CREATE TABLE PM_USER,這個段就是數據段,而CREATE INDEX ON PM_USER(NAME),ORACLE同樣會分配一個段給這個索引,但這是一個索引段了.查詢段的信息可以通過數據字典: SELECT * FROM USER_SEGMENTS來獲得,
1.4 表空間:包含段,區及塊.表空間的數據物理上儲存在其所在的數據文件中.一個數據庫至少要有一個表空間.
當我們創建了一個表,即使我沒有插入任何一行記錄,ORACLE還是給它分配了8個塊.當然這個跟建表語句的INITIAL 參數及MINEXTENTS參數有關:請看TEST_TAB的存儲參數:
STORAGE
(
INITIAL 64K
MINEXTENTS 1
MAXEXTENTS UNLIMITED
);
也就是說,在這個對象創建以後,ORACLE至少給它分配一個區,初始大小是64K,一個標準塊的大小是8K,剛好是8個BLOCK.
2. 什麼是高水線(High Water Mark)
所有的oracle段都有一個在段內容納數據的上限,我們把這個上限稱爲"high water mark"或HWM。這個HWM是一個標記,用來說明已經有多少沒有使用的數據塊分配給這個segment。HWM通常增長的幅度爲一次5個數據塊,原則上HWM只會增大,不會縮小,即使將表中的數據全部刪除,HWM還是爲原值,由於這個特點,使HWM很象一個水庫的歷史最高水位,這也就是HWM的原始含義,當然不能說一個水庫沒水了,就說該水庫的歷史最高水位爲0。但是如果我們在表上使用了truncate命令,則該表的HWM會被重新置爲0。
如何知道一個表的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 代表分配給該表,但是在水線以上的數據庫塊,即從來沒有使用的數據塊.
示例進行說明:
SQL>SELECT segment_name,segment_type,blocks FROM dba_segments
WHERE segment_name='TEST';
SEGMENT_NA SEGMENT_TYPE BLOCKS
---------- ------------------ ----------
TEST TABLE 8
TEST TABLE 8
SQL> ANALYZE TABLE TEST COMPUTE STATISTICS;
表已分析。
SQL> SELECT table_name,num_rows,blocks,empty_blocks FROM user_tables
WHERE table_name='TEST';
TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS
------------------------ -------------------- ---------- ------------
TEST 10 1 6
注意:BLOCKS + EMPTY_BLOCKS (6+1=7)比DBA_SEGMENTS.BLOCKS(8)少一個數據庫塊,這是因爲有一個數據庫塊被保留用作segment header。
DBA_SEGMENTS.BLOCKS 表示分配給這個表的所有的數據庫塊的數目。USER_TABLES.BLOCKS 表示已經使用過的數據庫塊的數目。
SQL> SELECT COUNT (DISTINCT DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid) ||
2 DBMS_ROWID.ROWID_RELATIVE_FNO(rowid)) "Used"FROM TEST;
Used
----------
1
SQL> DELETE from TEST;
已刪除10行。
SQL> COMMIT;
提交完成。
SQL> ANALYZE TABLE TEST ESTIMATE STATISTICS;
表已分析。
SQL> SELECT table_name,num_rows,blocks,empty_blocks FROM user_tables
WHERE table_name='TEST';
TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS
------------------ ------------ --- ---------- - --------------------
TEST 0 1 6
SQL> SELECT COUNT (DISTINCT DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid) ||
2 DBMS_ROWID.ROWID_RELATIVE_FNO(rowid)) "Used" FROM TEST;
Used
----------
0
注:Used = 0 這表名沒有任何數據庫塊容納數據,即表中無數據
SQL> TRUNCATE TABLE TEST;
表被截斷。
SQL> ANALYZE TABLE TEST COMPUTE STATISTICS;
表已分析。
SQL> SELECT table_name,num_rows,blocks,empty_blocks FROM user_tables
WHERE table_name='TEST';
TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS
---------------------------------------- ---------- ------------
TEST 0 0 7
SQL> SELECT segment_name,segment_type,blocks FROM dba_segments
WHERE segment_name='TEST';
SEGMENT_NA SEGMENT_TYPE BLOCKS
-------------------- ------------------------- -------------
TEST TABLE 8
TEST TABLE 8
注意:TRUNCATE命令回收了由delete命令產生的空閒空間,注意該表分配的空間由原先的1024塊降爲512塊。爲了保留由delete命令產生的空閒空間,可以使用TRUNCATE TABLE TEST REUSE STORAGE用此命令後,該表還會是原先的1024塊。
3. 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,但是在Oracle 9i Release1才添加的自動段空間管理(Automatic Segment Space Management)中,又有了一個低HWM的概念出來。爲什麼有了HWM還又有一個低HWM呢,這個是因爲自動段空間管理的特性造成的。在手段段空間管理中,當數據插入以後,如果是插入到新的數據塊中,數據塊就會被自動格式化等待數據訪問。而在自動段空間管理中,數據插入到新的數據塊以後,數據塊並沒有被格式化,而是在第一次訪問這個數據塊的時候才格式化這個塊。所以我們又需要一條水位線,用來標示已經被格式化的塊。這條水位線就叫做低HWM。一般來說,低HWM肯定是低於等於HWM的。
4. 修正ORACLE表的高水位線
在ORACLE中,執行對錶的刪除操作不會降低該表的高水位線。而全表掃描將始終讀取一個段(extent)中所有低於高水位線標記的塊。如果在執行刪除操作後不降低高水位線標記,則將導致查詢語句的性能低下。
下面的方法都可以降低高水位線標記。
1. 執行表重建指令 alter table table_name move;
在線轉移表空間ALTER TABLE ... MOVE TABLESPACE ..
當你創建了一個對象如表以後,不管你有沒有插入數據,它都會佔用一些塊,ORACLE也會給它分配必要的空間.同樣,用ALTER TABLE MOVE釋放自由空間後,還是保留了一些空間給這個表.
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
注:這證明,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會自動增大。
例如,通過直接加載插入(用 APPEND 提示插入)或通過 SQL*LOADER 直接路徑 數據塊直接置於 HWM 之上。它下面的空間就浪費掉了。