在實際的工作中我們經常會碰到一些Oracle資料庫效能較低的問題,當然,引起Oracle資料庫效能較低的原因是多方面的,我們能夠通過一些正確的設計和診斷來盡量的避免一些Oracle資料庫效能不好,Row Migration (行遷移) & Row Chaining (行連結)就是其中我們可以盡量避免的引起Oracle資料庫效能低下的潛在問題。通過合理的診斷行遷移/行連結,我們可以較大幅度上提高Oracle資料庫的效能。
那究竟什麼是行遷移/行連結呢,先讓我們從Oracle的block開始談起。
作業系統的最小讀寫操作單元是作業系統的block,所以當建立一個Oracle資料庫的時候我們應該講資料庫的block size設定成為作業系統的block size的整數倍,Oracle block是Oracle資料庫中讀寫操作的最小單元,Oracle9i之前的Oracle資料庫版本中Oracle block一旦在建立資料庫的時候被設定後就沒法再更改。為了在建立資料庫之前確定一個合理的Oracle block的大小,我們需要考慮一些因素,例如資料庫本身的大小以及並發事務的數量等。使用一個合適的Oracle block大小對於資料庫的調優是非常重要的。Oracle block的結構如下圖所示:
圖一:Oracle Block結構圖
由上圖我們可以看出,一個Oracle block由三個部分組成,分別是資料區塊頭、自由空間、實際資料三部份組成。
資料區塊頭:主要包含有資料區塊地址的一些基本資料和段的類型,以及表和包含有資料的實際行的地址。
自由空間:是指可以為以後的更新和插入操作分配的空間,大小由PCTFREE和PCTUSED兩個參數影響。
實際資料:是指在行記憶體儲的實際資料。
當建立或者更改任何錶和索引的時候,Oracle在空間控制方面使用兩個儲存參數:
PCTFREE:為將來更新已經存在的資料預留空間的百分比。
本文URL地址:http://www.bianceng.cn/database/Oracle/201410/45554.htm
PCTUSED:用於為插入一新行資料的最小空間的百分比。這個值決定了塊的可用狀態。可用的塊時可以執行插入的塊,不可用狀態的塊只能執行刪除和修改,可用狀態的塊被放在freelist中。
當表中一行的資料不能在一個資料block中放入的時候,這個時候就會發生兩種情況,一種是行連結,另外一種就是行遷移了。
行連結產生在第一次插入資料的時候如果一個block不能存放一行記錄的情況下。這種情況下,Oracle將使用連結一個或者多個在這個段中保留的block儲存這一行記錄,行連結比較容易發生在比較大的行上,例如行上有LONG、LONG RAW、LOB等資料類型的欄位,這種時候行連結是不可避免的會產生的。
當一行記錄初始插入的時候事可以儲存在一個block中的,由於更新操作導致行長增加了,而block的自由空間已經完全滿了,這個時候就產生了行遷移。在這種情況下,Oracle將會遷移整行資料到一個新的block中(假設一個block中可以儲存下整行資料),Oracle會保留被遷移行的原始指標指向新的存允許存取資料的block,這就意味著被遷移行的ROW ID是不會改變的。
當發生了行遷移或者行連結,對這行資料操作的效能就會降低,因為Oracle必須要掃描更多的block來獲得這行的資訊。
下面舉例來具體說明行遷移/行連結的產生過程。
先建立一個pctfree為20和pctused為50的測試表:
create table test(
col1 char(20),
col2 number)
storage (
pctfree 20
pctused 50);
當插入一條記錄的時候,Oracle會在free list中先去尋找一個自由的塊,並且將資料插入到這個自由塊中。而在free list中存在的自由的塊是由pctfree值決定的。初始的空塊都是在free list中的,直到塊中的自由空間達到pctfree的值,此塊就會從free list中移走,而當此塊中的使用空間低於pctused的時候,此塊又被重新放到free list中。
Oracle使用free list機制可以大大的提高效能,對於每次的插入操作,Oracle只需要尋找free list就可以了,而不是去尋找所有的block來尋找自由空間。
假設第一次插入資料使用的一個空的block,如下圖所示:
圖二:Oracle空的block結構圖
假設插入第一條記錄的時候佔用一個block的10%的空間(除去block頭佔去的大小),剩餘的自由空間90%大於pctfree20%,因此這個block還將繼續為下次的插入操作提供空間。
再連續插入七條記錄,使block的剩餘自由空間剩下20%,此時,這個block將要從free list中移走,如果再插入記錄,Oracle將再free list中尋找下一個空餘的block去存放後來插入的資料。
圖四:插入80%後的Oracle block結構圖
此時如果去更新第一條插入的記錄,使其行長增加15%,Oracle將會使用這個block中剩餘的20%的自由空間來存放此行資料,如果再更新第二條記錄,同樣的使其行長增加15%,而此block中只剩下5%的自由空間,不夠存放更新的第二條記錄,於是Oracle會在free list中尋找一個有自由空間(10%+15%)的block來存放這行記錄的block去儲存,在原來的block中儲存了指向新的block的指標,原來這行記錄的ROW ID保持不變,這個時候就產生了行遷移。
而當我們插入一條新紀錄的時候,如果一個blcok不足以存放下這條記錄,Oracle就會尋找一定數量的block一起來容納這條新的記錄,這個時候就產生了行連結,行連結主要產生在LOB、CLOB、BLOB和大的VA行連結HAR2資料類型上。
具體我們通過下面的一個實驗來查看行連結和行遷移是如何產生並在資料檔案中體現出來的。
先查看ALLAN這個資料表空間的資料檔案號,為了便於測試,我只建立了一個資料檔案。
SQL> select file_id from dba_data_files where tablespace_name='ALLAN';
FILE_ID
----------
23
建立一個測試表test:
SQL> create table test ( x int primary key, a char(2000), b char(2000), c char(2000), d char(2000), e char(2000) ) tablespace allan;
Table created.
因為我的資料庫的db_block_size是8K,所以我建立的表有五個欄位,每個佔2000個位元組,這樣一行記錄大約10K,就能超過一個block的大小了。
然後插入一行記錄,只有一個欄位的:
SQL> insert into test(x) values (1);
1 row created.
SQL> commit;
Commit complete.
尋找這行記錄所在的block,並dump出來:
SQL> select dbms_rowid.rowid_block_number(rowid) from test;
DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
------------------------------------
34
SQL> alter system dump datafile 23 block 34;
System altered.
在udump目錄下查看trace檔案的內容如下:
Start dump data blocks tsn: 34 file#: 23 minblk 34 maxblk 34
buffer tsn: 34 rdba: 0x05c00022 (23/34)
scn: 0x0000.013943f3 seq: 0x01 flg: 0x02 tail: 0x43f30601
frmt: 0x02 chkval: 0x0000 type: 0x06=trans data
Block header dump: 0x05c00022
Object id on Block? Y
seg/obj: 0x3ccd csc: 0x00.13943ef itc: 2 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x000a.02e.00000ad7 0x00800036.03de.18 --U- 1 fsc 0x0000.013943f3