Oracle 高水位(HWM)

yyp2009發表於2011-05-24

Oracle 高水位(HWM) 收藏
 

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。

 

 

 

 

 

 

示例:

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。
 

 

 

 

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 之上。它下面的空間就浪費掉了。

 

 

本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/tianlesoftware/archive/2009/10/21/4707900.aspx

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/13750068/viewspace-696193/,如需轉載,請註明出處,否則將追究法律責任。

相關文章