你是否了解Oracle空閑數(shù)據(jù)塊
在向大家詳細(xì)介紹Oracle空閑數(shù)據(jù)塊之前,首先讓大家了解下回滾段存儲的數(shù)據(jù),然后全面介紹Oracle空閑數(shù)據(jù)塊,希望對大家有用。在這里我們要說一下回滾段存儲的數(shù)據(jù),假如是delete操作,則回滾段將會記錄整個行的數(shù)據(jù),假如是update,則回滾段只記錄行被修改了的字段的變化前的數(shù)據(jù)(前映像),也就是沒有被修改的字段是不會被記錄的,假如是insert,則回滾段只記錄插入記錄的rowid。
這樣假如事務(wù)提交,那回滾段中簡單標(biāo)記該事務(wù)已經(jīng)提交;假如是回退,則如果操作是delete,回退的時候把回滾段中數(shù)據(jù)重新寫回數(shù)據(jù)塊,操作如果是update,則把變化前數(shù)據(jù)修改回去,操作如果是insert,則根據(jù)記錄的rowid把該記錄刪除。注意,檢查點(diǎn)除了觸發(fā)LGWR和DBWN向數(shù)據(jù)塊頭部寫SCN和COMMIT SCN,檢查點(diǎn)還向控制文件和數(shù)據(jù)文件頭部寫SCN,而用戶的DML和COMMIT僅是向數(shù)據(jù)塊頭部寫SCN和COMMIT SCN而不更新控制文件和數(shù)據(jù)文件的SCN,SMON的前滾是以文件頭部的SCN為起始點(diǎn)的也就是從前一個檢查點(diǎn)開始,SMON的回滾是回滾所有回滾段中未標(biāo)識為已提交的數(shù)據(jù)塊,用戶的回滾是回滾與此事務(wù)有關(guān)的回滾段中未標(biāo)識為已提交的數(shù)據(jù)塊。
下面我們要講DBWN如何來寫數(shù)據(jù)文件,在寫數(shù)據(jù)文件前首先要找到可寫的Oracle空閑數(shù)據(jù)塊,Oracle空閑數(shù)據(jù)塊可以通過Freelist或BITMAP來維護(hù),它們位于一個段的頭部用來標(biāo)識當(dāng)前段中哪些數(shù)據(jù)塊可以進(jìn)行INSERT。在本地管理表空間中Oracle自動管理分配給段的區(qū)的大小,只在本地管理的表空間中才能選用段自動管理,采用自動段空間管理的本地管理表空間中的段中的Oracle空閑數(shù)據(jù)塊的信息就存放在段中某些區(qū)的頭部,使用位圖來管理(最普通的情況是一個段的第一個區(qū)的第一個塊為FIRST LEVEL BITMAP BLOCK,第二個塊為SECOND LEVEL BITMAP BLOCK,第三個塊為PAGETABLE SEGMENT HEADER,再下面的塊為記錄數(shù)據(jù)的數(shù)據(jù)塊,F(xiàn)IRST LEVEL BITMAP BLOCK的父數(shù)據(jù)塊地址指向SECOND LEVEL BITMAP BLOCK,SECOND LEVEL BITMAP BLOCK的父數(shù)據(jù)塊地址指向PAGETABLE SEGMENT HEADER,F(xiàn)IRST LEVEL BITMAP BLOCK記錄了它所管理的所有塊(包括頭部三個塊,不僅僅指數(shù)據(jù)塊)的狀態(tài),標(biāo)識的狀態(tài)有Metadata、75-100% free、50-75% free、25-50% free、0-25% free、full、unformatted,在SECOND LEVEL BITMAP BLOCK中有一個列表,記錄了它管理的FIRST LEVEL BITMAP BLOCK,PAGETABLE SEGMENT HEADER中記錄的內(nèi)容比較多,除了記錄了它管理的SECOND LEVEL BITMAP BLOCK,還記錄了各個區(qū)的首塊地址以及各個區(qū)的DB BLOCK的個數(shù),段的各個區(qū)所對應(yīng)的FIRST LEVEL BITMAP BLOCK的塊地址以及區(qū)里面記錄數(shù)據(jù)的數(shù)據(jù)塊的起始地址。
如果一個區(qū)擁有很多塊,這時會在一個區(qū)里出現(xiàn)兩個或多個FIRST LEVEL BITMAP BLOCK,這些FIRST LEVEL BITMAP BLOCK分別管理一個區(qū)中的一些塊,當(dāng)區(qū)的數(shù)據(jù)塊比較少時,一個區(qū)的FIRST LEVEL BITMAP BLOCK可以跨區(qū)管理多個區(qū)的數(shù)據(jù)塊,BITMAP BOLCK最多為三級)。采用手動管理的本地管理表空間中的段和數(shù)據(jù)字典管理的表空間中的段中的Oracle空閑數(shù)據(jù)塊的管理都使用位于段頭部的空閑列表來管理,例如SYSTEM表空間是本地管理表空間,但是它是采用了手動段空間管理,所以也是用Freelist來管理段中的Oracle空閑數(shù)據(jù)塊的。空閑列表是一個邏輯上的鏈表,在段的HEADER BLOCK中記錄了一個指向第一個空閑塊的BLOCK ADDRESS,第一個DB BLOCK中同時也記錄了指向下一個空閑塊的BLOCK ADDRESS。
以此形成一個單向鏈表。如果段上有兩個FREE LIST則會在段頭部的HEADER BLOCK存有兩個指針分別指向兩個空閑塊并建立獨(dú)立的兩個單向鏈表。空閑列表的工作方式:首先當(dāng)建立一個段時,初始分配的第一個區(qū)的第一個塊會成為段的頭塊,初始分配的第一個區(qū)的其它塊將全部加入空閑列表,再次擴(kuò)展一個區(qū)時,這個區(qū)中的塊立即全部加入空閑列表,擴(kuò)展一次加入一次。與位圖管理不同的是用空閑列表時區(qū)的頭部將不記錄區(qū)里面空閑塊的信息。當(dāng)其中空閑空間小于PCTFREE設(shè)置的值之后,這個塊從空閑列表刪除,即上一個指向它的塊中記錄的下一個空閑塊地址更改為其它空閑塊的地址,使得這個塊類似于被短路,當(dāng)這個塊中的內(nèi)容降至PCTUSED設(shè)置的值之下后,這個數(shù)據(jù)塊被再次加入空閑列表,而且是加入到空閑列表前端,即頭塊直接指向它,它再指向原頭塊指向的空閑塊,位于空閑列表中的數(shù)據(jù)塊都是可以向其中INSERT的塊,但是INSERT都是從空閑列表指向的第一個塊開始插入,當(dāng)一個塊移出了空閑列表,但只要其中還有保留空間就可以進(jìn)行UPDATE,當(dāng)對其中一行UPDATE一個大數(shù)據(jù)時,如果當(dāng)前塊不能完全放下整個行,只會把整個行遷移到一個新的數(shù)據(jù)塊,并在原塊位置留下一個指向新塊的指針,這叫行遷移。如果一個數(shù)據(jù)塊可以INSERT,當(dāng)插入一個當(dāng)前塊裝不下的行時,這個行會溢出到兩個或兩個幾上的塊中,這叫行鏈接。
如果用戶的動作是INSERT則服務(wù)器進(jìn)程會先鎖定Freelist,然后找到第一個空閑塊的地址,再釋放Freelist,當(dāng)多個服務(wù)器進(jìn)程同時想要鎖定Freelist時即發(fā)生Freelist的爭用,也就是說多個進(jìn)程只在同時INSERT時才會發(fā)生Freelist爭用,可以在非采用自動段空間管理的表空間中創(chuàng)建表時指定Freelist的個數(shù),默認(rèn)為1,如果是在采用自動段空間管理的表空間中創(chuàng)建表,即使指定了Freelist也會被忽略,因?yàn)榇藭r將使用BITMAP而不是Freelist來管理段中的空閑空間。采用自動段空間管理還會忽略的參數(shù)有PCTUSED和Freelist GROUPS。如果用戶動作是UPDATE或DELETE等其它操作,服務(wù)器進(jìn)程將不會使用到Freelist和BITMAP,因?yàn)椴灰ふ乙粋€空閑塊,而使用鎖的隊(duì)列。對數(shù)據(jù)塊中數(shù)據(jù)操作必須使用transaction entries,即事務(wù)入口。
在建立段時我們可以通過MINTRANS和MAXTRANS參數(shù)指定它的最大值和最小值,MAXTRANS規(guī)定了在段中每一個塊上最大并發(fā)事務(wù)數(shù)量,可以輸入1到255之間的值。我們可以把它比喻為是一些長在塊頭部的事務(wù)插座,每個插座后面是一個可以伸縮的操作手,當(dāng)事務(wù)進(jìn)程插到一個插座上時相當(dāng)于找到一個可以操作數(shù)據(jù)塊中數(shù)據(jù)行的操作手,通過這個操作手,事務(wù)進(jìn)程可以對塊中數(shù)據(jù)進(jìn)行INSERT、UPDATE、DELETE等操作。在沒有超過MAXTRANS設(shè)定的最大值時,如果transaction entries不夠用,則會在塊上自動分配一個,但不會影響其它塊中的transaction entries數(shù)量。只不過INSERT操作必須要先找到空閑塊然后才能INSERT。
那么DBWN是根據(jù)什么順序來寫DB BUFFER中的臟數(shù)據(jù)的呢?Oracle從8I開始加入新的數(shù)據(jù)結(jié)構(gòu)--檢查點(diǎn)隊(duì)列(Buffer Checkpoint Queue)。檢查點(diǎn)隊(duì)列是一個鏈接隊(duì)列。這個隊(duì)列的按照Buffer塊第一次被修改的順序排列,分別指向被修改的Buffer塊。在DB_Buffer中的數(shù)據(jù)被第一次被修改時,會記錄所生成的REDO LOG條目的位置RBA作為該Buffer的Low RBA,記錄在該Buffer的頭部(Buffer Header),如果該數(shù)據(jù)繼續(xù)被修改,則把該塊修改的最新的REDO LOG的RBA作為High RBA記錄在該Buffer的頭部。如果DB_Buffer中的塊沒有被修改的數(shù)據(jù),則該塊的頭部不會有Low RBA和High RBA的信息。檢查點(diǎn)隊(duì)列按照被修改塊的Low RBA的遞增值鏈接修改塊,沒有被修改的塊因?yàn)闆]有Low RBA,而不會加入到檢查點(diǎn)隊(duì)列中。
在沒有檢查點(diǎn)發(fā)生時DBWR就按照檢查點(diǎn)隊(duì)列的Low RBA的升序,將被修改的塊寫入到數(shù)據(jù)文件中。當(dāng)塊被寫入到數(shù)據(jù)文件后,該塊會從檢查點(diǎn)隊(duì)列中斷開。DBWR繼續(xù)寫下一個塊。CKPT進(jìn)程每三秒記錄檢查點(diǎn)隊(duì)列中對應(yīng)的最小Low RBA到控制文件中,也就是更新控制文件中的CheckPointRBA,當(dāng)實(shí)例崩潰時,恢復(fù)將從CheckPointRBA所指向的日志位置開始。這就是"增量檢查點(diǎn)"的行為和定義。CKPT進(jìn)程也會記錄檢查點(diǎn)位置到數(shù)據(jù)文件的頭部,但是只是日志切換時才寫。而不是每三秒。當(dāng)檢查點(diǎn)發(fā)生時,DBWN不會一直不停的寫DB BUFFER中臟數(shù)據(jù),它將寫到檢查點(diǎn)隊(duì)列的開始塊的Low RBA的值大于該檢查點(diǎn)的Checkpoint RBA的值時停止寫入,然后完成這次檢查點(diǎn),CKPT進(jìn)程將記錄該檢查點(diǎn)的有關(guān)信息到控制文件中去。以上介紹Oracle空閑數(shù)據(jù)塊。
【編輯推薦】