Oracle 高水位(HWM: High Water Mark) 說明

準備知識:ORACLE的邏輯存儲管理. 數據庫

       ORACLE在邏輯存儲上分4個粒度表空間區 和 塊. session

 

       1.1 粒度最小的存儲單位,如今標準的塊大小是8K,ORACLE每一次I/O操做也是按塊來操做的,也就是說當ORACLE從數據文件讀數據時,是讀取多少個塊,而不是多少行.  每個Block裏能夠包含多個row. oracle

 

       1.2 由一系列相鄰的塊而組成,這也ORACLE空間分配的基本單位,舉個例子來講,當咱們建立一個表Dave,首先ORACLE會分配一區的空間給這個表,隨着不斷的INSERT數據到Dave,原來的這個區容不下插入的數據時,ORACLE是以區爲單位進行擴展的,也就是說再分配多少個區給Dave,而不是多少個塊. app

 

       1.3 由一系列的區所組成通常來講當建立一個對象時(,索引),就會分配一個段給這個對象因此從某種意義上來講,段就是某種特定的數據.CREATE TABLE Dave,這個段就是數據段,CREATE INDEX ON Dave(NAME), ORACLE一樣會分配一個段給這個索引,但這是一個索引段了.查詢段的信息能夠經過數據字典: SELECT * FROM USER_SEGMENTS來得到. less

 

       1.4 表空間包含段,區及塊.表空間的數據物理上儲存在其所在的數據文件中.一個數據庫至少要有一個表空間. ide

 

表空間(tableSpace) (segment) 盤區(extent) (block) 關係 oop

http://blog.csdn.net/tianlesoftware/archive/2009/12/08/4962476.aspx 性能

 

 

       當咱們建立了一個表,即便我沒有插入任何一行記錄, ORACLE仍是給它分配了8個塊固然這個跟建表語句的INITIAL 參數及MINEXTENTS參數有關如: 測試

STORAGE ui

(

INITIAL 64K

MINEXTENTS 1

MAXEXTENTS UNLIMITED

);

 

       也就是說,在這個對象建立之後,ORACLE至少給它分配一個區,初始大小是64K,一個標準塊的大小是8K,恰好是8BLOCK.

 

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:

1Above the HWM

       These blocks are unformatted and have never been used.

2Below the HWM

       These blocks are in one of the following states:

       1Allocated, but currently unformatted and unused

       2Formatted and contain data

       3Formatted 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.

 

Figure 12-24 HWM and Low HWM

 

       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.

 

Figure 12-25 HWM and Low HWM

 

       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. InFigure 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 重建表

       複製要保留的數據到臨時表tdrop原表,而後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 unusedHWM以上全部沒使用的空間釋放

       2) 若是MINEXTENT >HWM 則釋放MINEXTENTS 以上的空間。若是要釋放HWM以上的空間則使用KEEP 0

       SQL>alter table tablesname deallocate unused keep 0;

       3truncate table drop storage(缺省值)命令能夠將MINEXTENT 之上的空間徹底釋放(交還給操做系統),而且重置HWM

       4)若是僅是要移動HWM,而不想讓表長時間鎖住,能夠用truncate table reuse storage,僅將HWM重置。

       5ALTER TABLE MOVE會將HWM移動,但在MOVE時須要雙倍的表空間,並且若是表上有索引的話,須要重構索引

       6DELETE表不會重置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 ORACLEHWM來界定一個段中使用的塊和未使用的塊.

 

舉個例子來講,當咱們建立一個表時,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 區別

在下面2blog 有說明:         

       http://blog.csdn.net/robinson1988/archive/2010/09/07/5868742.aspx

 

       alter table moveshrink 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 .

 

       Movesegment的底部開始,move這些rowssegment的頭部。Shrink則是delete/insert相結合,這樣會產生很是多的UNDOREDO

 

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). clustercluster table,或具備Longlob類型列的對象 不起做用。

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,速度相對也慢一點。

相關文章
相關標籤/搜索