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