Oracle-HWM(High Water Mark) 高水位解读
讀前須知:Oracle的邏輯存儲管理
ORACLE在邏輯存儲上分4個粒度 ,由大到小為: 表空間, 段, 區(qū) 和 塊.
塊Block
塊:是粒度最小的存儲單位,現(xiàn)在標(biāo)準(zhǔn)的塊大小是8K,ORACLE每一次I/O操作也是按塊來操作的,也就是說當(dāng)ORACLE從數(shù)據(jù)文件讀數(shù)據(jù)時,是讀取多少個塊,而不是多少行. 每一個Block里可以包含多個row.
數(shù)據(jù)塊的大小是通過kb字節(jié)個數(shù)來指定的,默認為8KB。相關(guān)參數(shù)為db_block_size
SQL> show parameter db_block_sizeNAME TYPE VALUE ----------------------- --------------------- --------- db_block_size integer 8192區(qū)Extent
由一系列相鄰的塊而組成,這也是ORACLE空間分配的基本單位.
區(qū)extent是比數(shù)據(jù)塊大一級的存儲結(jié)構(gòu),表示的是一連串連續(xù)的數(shù)據(jù)塊集合。
在進行存儲數(shù)據(jù)信息的時候,Oracle將分配數(shù)據(jù)塊進行存儲,但是不能保證所有分配的數(shù)據(jù)塊都是連續(xù)的結(jié)構(gòu)。
所以,出現(xiàn)分區(qū)extent的概念,表示一系列連續(xù)的數(shù)據(jù)塊集合。
舉個例子來說,當(dāng)我們創(chuàng)建一個表時,首先ORACLE會分配一區(qū)的空間給這個表,隨著數(shù)據(jù)不斷地增長,原來的這個區(qū)容不下插入的數(shù)據(jù)時,ORACLE是以區(qū)為單位進行擴展的,也就是說再分配多少個區(qū)給這個表,而不是多少個塊.
視圖dba_extents(或者all_extents、user_extents)是我們研究分區(qū)結(jié)構(gòu)和存儲構(gòu)成的重要手段。
段Segment
段: 是由一系列的區(qū)extent所組成
數(shù)據(jù)段是與數(shù)據(jù)庫對象相對應(yīng),一般一個數(shù)據(jù)庫對象對應(yīng)一個數(shù)據(jù)段。
多個extent是對應(yīng)一個數(shù)據(jù)段,每個數(shù)據(jù)段實際上就是數(shù)據(jù)庫一個對象的代表。
一般來說, 當(dāng)創(chuàng)建一個對象時(表,索引),就會分配一個段給這個對象.
從dba_segments、user_segments視圖中,可以比較清楚看清數(shù)據(jù)段的結(jié)構(gòu)。
表空間Tablespace
TableSpace是存儲結(jié)構(gòu)中的最高層結(jié)構(gòu)。建立一個表空間的時候,是需要指定存儲的文件。一個表空間可以指定多個數(shù)據(jù)文件,多個文件可以在不同的物理存儲上。也就是說,表空間是可以跨物理存儲的。
但是有一點就是,表空間下一級對象數(shù)據(jù)段的存儲,是不能指定存儲在那個文件里的。所以,要想讓數(shù)據(jù)對象訪問IO負載均衡,需要指定不同的數(shù)據(jù)對象在不同的表空間里。這也就是為什么將數(shù)據(jù)表和索引建立在不同的表空間的原因。
表空間通過v$tablespace進行訪問
其中兩個參數(shù)需要注意一下。
一個是bigfile,是一個標(biāo)志位,標(biāo)志表空間是不是所謂的大文件表空間。
大文件表空間是在10g中推出的一個新特性,處于性能考慮,可以設(shè)置表空間為大文件表空間,存儲超過百T的數(shù)據(jù),但是要求數(shù)據(jù)文件只能有一個。
另一個是flashback_on,表示表空間的閃回特性是否開啟。
還有 dba_tablespaces 、 user_tablespaces。
Oracle表段中的高水位線HWM
在數(shù)據(jù)庫表剛建立的時候,由于沒有任何數(shù)據(jù),所以這個時候水位線是空的,也就是說HWM為最低值。
當(dāng)插入了數(shù)據(jù)以后,高水位線就會上漲,但如果你采用delete語句刪除數(shù)據(jù)的話,數(shù)據(jù)雖然被刪除了,但是高水位線卻沒有降低,還是你剛才刪除數(shù)據(jù)以前那么高的水位。
也就是說,這條高水位線在日常的增刪操作中只會上漲,不會下跌。
HWM通常增長的幅度為一次5個數(shù)據(jù)塊.
Select語句會對表中的數(shù)據(jù)進行一次掃描,但是究竟掃描多少數(shù)據(jù)存儲塊呢,這個并不是說數(shù)據(jù)庫中有多少數(shù)據(jù),Oracle就掃描這么大的數(shù)據(jù)塊,而是Oracle會掃描高水位線以下的數(shù)據(jù)塊。
試想一下,新建的一個空表,Select一下,由于高水位線HWM在最低的0位置上,所以沒有數(shù)據(jù)塊需要被掃描,掃描時間會極短。
如果這個時候你首先插入了一千萬條數(shù)據(jù),然后再用delete語句刪除這一千萬條數(shù)據(jù)。由于插入了一千萬條數(shù)據(jù),所以這個時候的高水位線就在一千萬條數(shù)據(jù)這里。后來刪除這一千萬條數(shù)據(jù)的時候,由于delete語句不影響高水位線,所以高水位線依然在一千萬條數(shù)據(jù)這里。
這個時候再一次用select語句進行掃描,雖然這個時候表中沒有數(shù)據(jù),但是由于掃描是按照高水位線來的,所以需要把一千萬條數(shù)據(jù)的存儲空間都要掃描一次,也就是說這次掃描所需要的時間和掃描一千萬條數(shù)據(jù)所需要的時間是一樣多的。所以有時候有人總是經(jīng)常說,怎么我的表中沒有幾條數(shù)據(jù),但是還是這么慢呢,這個時候其實奧秘就是這里的高水位線了。
那有沒有辦法讓高水位線下降呢 ? 采用TRUNCATE語句刪除一個表的數(shù)據(jù)的時候,類似于重新建立了表,不僅把數(shù)據(jù)都刪除了,還把HWM給清空恢復(fù)為0。
所以如果需要把表清空,在有可能利用TRUNCATE語句來刪除數(shù)據(jù)的時候就利用TRUNCATE語句來刪除表,特別是那種數(shù)據(jù)量有可能很大的臨時存儲表。
在手動段空間管理(Manual Segment Space Management)中,段中只有一個HWM,
但是在Oracle 9i Release1才添加的自動段空間管理(Automatic Segment Space Management)中,又有了一個低HWM的概念出來。
為什么有了HWM還又有一個低HWM呢,這個是因為自動段空間管理的特性造成的。在手段段空間管理中,當(dāng)數(shù)據(jù)插入以后,如果是插入到新的數(shù)據(jù)塊中,數(shù)據(jù)塊就會被自動格式化等待數(shù)據(jù)訪問。
而在自動段空間管理中,數(shù)據(jù)插入到新的數(shù)據(jù)塊以后,數(shù)據(jù)塊并沒有被格式化,而是在第一次訪問這個數(shù)據(jù)塊的時候才格式化這個塊。
所以我們又需要一條水位線,用來標(biāo)示已經(jīng)被格式化的塊。這條水位線就叫做低HWM。一般來說,低HWM肯定是低于等于HWM的。
降低ORACLE表的高水位線
在ORACLE中,執(zhí)行對表的刪除操作不會降低該表的高水位線。而全表掃描將始終讀取一個段(extent)中所有低于高水位線標(biāo)記的塊。如果在執(zhí)行刪除操作后不降低高水位線標(biāo)記,則將導(dǎo)致查詢語句的性能低下。
rebuild, truncate, shrink,move 等操作會降低高水位。
執(zhí)行表重建指令 alter table table_name move
在線轉(zhuǎn)移表空間ALTER TABLE … MOVE TABLESPACE ..
當(dāng)你創(chuàng)建了一個對象如表以后,不管你有沒有插入數(shù)據(jù),它都會占用一些塊,ORACLE也會給它分配必要的空間.
同樣,用ALTER TABLE MOVE釋放自由空間后,還是保留了一些空間給這個表.
ALTER TABLE … MOVE 后面不跟參數(shù)也行,不跟參數(shù)表還是在原來的表空間,Move后記住重建索引.
如果以后還要繼續(xù)向這個表增加數(shù)據(jù),沒有必要move, 只是釋放出來的空間,只能這個表用,其他的表或者segment無法使用該空間。
執(zhí)行alter table table_name shrink space-10g新功能
此命令為Oracle 10g新增功能,再執(zhí)行該指令之前必須允許行移動 alter table table_name enable row movement;
如果要同時壓縮表的索引,可以發(fā)布:alter table test_tab shrink space cascade
重建表
復(fù)制要保留的數(shù)據(jù)到臨時表t,drop原表,然后rename臨時表t為原表
用邏輯導(dǎo)入導(dǎo)出: Emp/Imp
Alter table table_name deallocate unused
DEALLOCATE UNUSED會釋放HWM上面的未使用空間,但是并不會釋放HWM下面的自由空間,也不會移動HWM的位置.
truncate(推薦使用)
truncate table xxx
HWM的特征
ORACLE用HWM來界定一個段中使用的塊和未使用的塊
當(dāng)我們創(chuàng)建一個表時,ORACLE就會為這個對象分配一個段.在這個段中,即使我們未插入任何記錄,也至少有一個區(qū)被分配,第一個區(qū)的第一個塊就稱為段頭(SEGMENT HEADE),段頭中就儲存了一些信息,HWM的信息就存儲在此.
我們不斷插入數(shù)據(jù)時,HWM會往不斷上移,這樣,在HWM之下的,就表示使用過的塊,HWM之上的就表示已分配但從未使用過的塊.
HWM在插入數(shù)據(jù)時,當(dāng)現(xiàn)有空間不足而進行空間的擴展時會向上移,但刪除數(shù)據(jù)時不會往下移.
ORACLE 不會釋放空間以供其他對象使用,有一條簡單的理由:由于空間是為新插入的行保留的,并且要適應(yīng)現(xiàn)有行的增長。被占用的最高空間稱為最高使用標(biāo)記 (HWM).
HWM的信息存儲在段頭當(dāng)中.
HWM本身的信息是儲存在段頭.
在段空間是手工管理方式時,ORACLE是通過FREELIST(一個單向鏈表)來管理段內(nèi)的空間分配.
在段空間是自動管理方式時(ASSM),ORACLE是通過BITMAP來管理段內(nèi)的空間分配.
ORACLE的全表掃描是讀取高水位標(biāo)記(HWM)以下的所有塊.
所以問題就產(chǎn)生了.當(dāng)用戶發(fā)出一個全表掃描時,ORACLE 始終必須從段一直掃描到 HWM,即使它什么也沒有發(fā)現(xiàn)。
該任務(wù)延長了全表掃描的時間。
當(dāng)用直接路徑插入行時,即使HWM以下有空閑的數(shù)據(jù)庫塊,鍵入在插入數(shù)據(jù)時使用了append關(guān)鍵字,則在插入時使用HWM以上的數(shù)據(jù)塊,此時HWM會自動增大。
例如,通過直接加載插入(用 APPEND 提示插入)或通過 SQL*LOADER 直接路徑 數(shù)據(jù)塊直接置于 HWM 之上。它下面的空間就浪費掉了。
栗子
數(shù)據(jù)庫版本 Connected to Oracle Database 11g Enterprise Edition Release 11.2.0.4.0
創(chuàng)建測試表
SQL> create table tt(id number);Table created此時表沒有分析,是原始的數(shù)據(jù),即8個數(shù)據(jù)塊。
--空的 SQL> SELECT segment_name,segment_type,blocks FROM dba_segments a WHERE a.segment_name = 'TT';SEGMENT_NAME SEGMENT_TYPE BLOCKS -------------------------------------------------------------------------------- ------------------ ------------空的 SQL> SELECT table_name,num_rows,blocks,empty_blocks FROM user_tables WHERE table_name='TT';TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS ------------------------------ ---------- ---------- ------------ TT向表中插入一些測試數(shù)據(jù)
SQL> declare2 i number;3 begin4 for i in 1 .. 10000 loop5 insert into tt values (i);6 end loop;7 commit;8 end;9 /PL/SQL procedure successfully completed重新查詢表的信息
此時表TT 占用的塊已經(jīng)是24個了.
但是user_tables 顯示的信息還是為空。 因為沒有做統(tǒng)計分析。
收集統(tǒng)計信息
SQL> exec DBMS_STATS.GATHER_TABLE_STATS(user,'TT');PL/SQL procedure successfully completed再此查詢一下
此時user_tables 已經(jīng)有了數(shù)據(jù),顯示的使用了20個數(shù)據(jù)塊。 但是empty_blocks 還是為空。 這里要注意的地方。 empty_blocks 這個字段只有使用analyze 收集統(tǒng)計信息之后才會有數(shù)據(jù)。
使用analyze搜集統(tǒng)計信息
SQL> analyze table tt compute statistics;Table analyzeddelete 數(shù)據(jù),不會降低高水位
可以發(fā)現(xiàn) 分析前后,blocks 和 empty_blocks 都沒有發(fā)生變化。
truncate 表,可以降低高水位
--truncate 表 SQL> truncate table tt;Table truncated--查詢段信息,blocks由24降到了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 0 20 4------------------------------------------------------------ ------------------------------------------------------------ --收集下表信息 SQL> exec dbms_stats.gather_table_stats(user,'TT');PL/SQL procedure successfully completed--重新統(tǒng)計下段信息,一樣 SQL> SELECT segment_name,segment_type,blocks FROM dba_segments WHERE segment_name='TT';SEGMENT_NAME SEGMENT_TYPE BLOCKS --------- ---------- ---------- TT TABLE 8--重新查詢表信息 BLOCKS 由20降為0, 但是empyt_blocks 還是4個 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 4--analyze 分析下 更改EMPTY_BLOCKS的值 SQL> analyze table tt compute statistics;Table analyzed--重新查詢段信息, 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 0 8SQL>總共8個數(shù)據(jù)塊,8個為空
Alter table move 和Shrink的區(qū)別
Shrink
在10g之后,整理碎片消除行遷移的新增功能shrink space
語法
alter table <table_name> shrink space [ <null> | compact | cascade ];- compact: 這個參數(shù)當(dāng)系統(tǒng)的負載比較大時可以用,不降低HWM。如果系統(tǒng)負載較低時,直接用alter table table_name shrink space就一步到位了
- cascade:這個參數(shù)是在shrink table的時候自動級聯(lián)索引,相當(dāng)于rebulid index。
基于普通表
--shrink必須開啟行遷移功能。 alter table table_name enable row movement ;--保持HWM,相當(dāng)于把塊中數(shù)據(jù)打結(jié)實了 alter table table_name shrink space compact;--回縮表與降低HWM alter table table_name shrink space;--回縮表與相關(guān)索引,降低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 表
shrink栗子
SQL> create table tt(id number);SQL> declarei number; beginfor i in 1 .. 10000 loopinsert into tt values (i);end loop;commit; end;SQL> exec DBMS_STATS.GATHER_TABLE_STATS(user,'TT');SQL> analyze table tt compute statistics;SQL> delete from tt;SQL> commit ;SQL> alter table tt enable row movement ; SQL> alter table tt shrink space; SQL> analyze table tt compute statistics;SQL> SELECT segment_name,segment_type,blocks FROM dba_segments WHERE segment_name='TT';SQL> SELECT table_name,num_rows,blocks,empty_blocks FROM user_tables WHERE table_name='TT';Move
通過desc table_name 來檢查表中是否有LOB 字段,
表中沒有l(wèi)ob字段
如果表沒有LOB字段, 直接 alter table move; 然后 rebuild index
表中包含了LOB字段
alter table owner.table_name move tablespace tablespace_name lob (lob_column) store as lobsegment tablespace tablespace_name;也可以單獨move lob,但是表上的index 同樣會失效. 所以在操作結(jié)束,需要對索引進行rebuild。
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.compositeFROM dba_indexes aLEFT JOINdba_ind_partitions bON a.owner = b.index_owner AND a.index_name = b.index_nameWHERE a.owner = '&owner' AND a.table_name = '&table_name';對于普通索引直接rebuild online nologging parallel,
對于分區(qū)索引,必須單獨rebuild 每個分區(qū),
對于組合分區(qū)索引,必須單獨rebuild 每個子分區(qū)。
總結(jié)
Move 通過移動數(shù)據(jù)來來降低HWM,因此需要更多的磁盤空間。
Shrink 通過delete 和 insert, 會產(chǎn)生較多的undo 和redo。
shrink space收縮到數(shù)據(jù)存儲的最小值,alter table move(不帶參數(shù))收縮到initial指定值,也可以用alter table test move storage(initial 500k)指定收縮的大小,這樣可以達到shrink space效果。
總之,使用Move 效率會高點,但是會導(dǎo)致索引失效。Shrink 會產(chǎn)生undo 和redo,速度相對也慢一點。
博文學(xué)習(xí)至David Dai 大神,再此向大神致敬!
總結(jié)
以上是生活随笔為你收集整理的Oracle-HWM(High Water Mark) 高水位解读的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Shell脚本攻略03-使用shell进
- 下一篇: Oracle优化02-锁和阻塞