讀與寫是每個數(shù)據(jù)庫提供的最基本的功能。當(dāng)數(shù)據(jù)庫中出現(xiàn)第一個進程時,總免不了要將數(shù)據(jù)從磁盤上加載到內(nèi)存中,一次數(shù)據(jù)庫的物理I/O由此發(fā)生。而這對應(yīng)著數(shù)據(jù)庫的讀事件。通常大多數(shù)情況下,數(shù)據(jù)庫中不僅會伴隨著大量的讀,也會產(chǎn)生大量的寫入更新操作,而類似寫入更新的操作則需要將修改后的數(shù)據(jù)寫回到磁盤中。
顯然,對于讀操作來說,總是依據(jù)需要讀取數(shù)據(jù)的請求從磁盤上或內(nèi)存中找到對應(yīng)滿足的數(shù)據(jù)返回給客戶端。也就是說,讀操作必定是伴隨著客戶端會話的請求而出現(xiàn)的。那么,寫操作在數(shù)據(jù)被更新/修改后是如何寫回到磁盤中的?
Oracle實例在啟動時在內(nèi)存中劃分了一塊區(qū)域,被稱作 SGA(System Global Area,系統(tǒng)全局區(qū))。其中共有6大內(nèi)存區(qū)域,緩沖數(shù)據(jù)庫中的數(shù)據(jù)部分的內(nèi)存區(qū)域則稱為 Database Buffer Cache(數(shù)據(jù)庫高速緩存區(qū))。
Database Buffer Cache 主要用來提高數(shù)據(jù)的訪問性能,避免訪問數(shù)據(jù)庫過程中出現(xiàn)大量的磁盤讀寫??梢姡?strong>Database Buffer Cache 存在的意義不僅在于緩存數(shù)據(jù)庫中讀的數(shù)據(jù),而且需要提升數(shù)據(jù)庫的寫性能。既然如此,數(shù)據(jù)被修改或更新后也不是立刻會寫回到磁盤中的,而應(yīng)該有一定的規(guī)則將數(shù)據(jù)緩存區(qū)中的數(shù)據(jù)寫回到磁盤中。而實現(xiàn)這一機制的,則是 DBWR(Database Writer)。
后臺進程-DBWR
DBWR 是Oracle數(shù)據(jù)庫中的一個后臺進程,用于將 Database Buffer Cache 中的被修改的數(shù)據(jù)寫回到磁盤中。在數(shù)據(jù)庫中,數(shù)據(jù)最先從磁盤上讀取數(shù)據(jù)并加載到內(nèi)存中,此時的數(shù)據(jù)并沒有被任何的用戶進程修改過,這種類型的數(shù)據(jù)稱為 Cleaning Data。而當(dāng)數(shù)據(jù)庫中的數(shù)據(jù)被用戶進程修改了某些字段的值后,因為數(shù)據(jù)是在內(nèi)存中被修改的,修改后的數(shù)據(jù)(內(nèi)存中)因為與磁盤上的數(shù)據(jù)文件中記錄的數(shù)據(jù)已經(jīng)發(fā)生了不同,因此這部分被修改過的數(shù)據(jù)被稱為 Dirty Data。當(dāng)內(nèi)存區(qū)域中的 Dirty Data 被 DBWR 進程寫入到磁盤上后,數(shù)據(jù)文件又恢復(fù) Clenaing 狀態(tài),這個過程稱為 刷臟。
內(nèi)存中的數(shù)據(jù)是無論如何也要寫回到磁盤中的,這一點是確定無疑的。但更困難的問題在于以什么樣的機制將數(shù)據(jù)寫回到磁盤。
針對這個問題,或許有很多中解決方案。或者寫入發(fā)生在數(shù)據(jù)修改的那一刻,或者定時定量將數(shù)據(jù)寫回到磁盤,或者由其他機制(進程)觸發(fā)寫入操作,或許都是可行的方案。
先說說寫入發(fā)生在數(shù)據(jù)修改的那一刻的情況,這種情況下數(shù)據(jù)庫中發(fā)生一次數(shù)據(jù)修改后,就需要立刻將數(shù)據(jù)寫回到磁盤中。顯然,一旦這么做會在數(shù)據(jù)庫中產(chǎn)生大量的物理寫入,而過多的物理I/O會降低數(shù)據(jù)庫的性能,顯然,修改即寫入無法滿足數(shù)據(jù)庫的性能要求,但這種方式或許是保障數(shù)據(jù)不會丟失的好辦法。
接著是定時定量將內(nèi)存中的數(shù)據(jù)寫回到磁盤中,這種方式也是 DBWR 機制中的一種。與修改即寫入不同的是,被修改的數(shù)據(jù)會在內(nèi)存中保留一段時間,直到達到數(shù)據(jù)庫后臺進程 DBWR 設(shè)定(默認)的時間閾值后,一次將內(nèi)存中被修改的數(shù)據(jù)塊寫回到磁盤中。相比修改即寫入,定時定量將內(nèi)存區(qū)域被修改的數(shù)據(jù)塊則可以避免可能發(fā)生在數(shù)據(jù)庫中的大量物理I/O問題,從而提高數(shù)據(jù)庫的訪問性能。
DBWR 后臺進程中也是定時定量對 數(shù)據(jù)庫高速緩存區(qū) 中的數(shù)據(jù)進行寫回到磁盤操作的。在Oracle中,DBWR 每隔3s啟動一次進程將數(shù)據(jù)寫回到磁盤中;另一種情況是當(dāng)內(nèi)存中的臟數(shù)據(jù)塊占比達到一定限制后,也會觸發(fā) DBWR 進程將臟數(shù)據(jù)刷回到磁盤中去。
再來說按規(guī)則(機制)將內(nèi)存中的數(shù)據(jù)寫回到磁盤的情況。這種方式類似于定時定量的方式,都是以某一種設(shè)定條件作為觸發(fā) DBWR 寫數(shù)據(jù)的依據(jù)。在Oracle中,則是使用 CKPT 檢查點作為觸發(fā) DBWR 的條件。
當(dāng)數(shù)據(jù)庫發(fā)生檢查點事件時,后臺進程 DBWR 會被啟動,并將當(dāng)前內(nèi)存區(qū)域中的臟數(shù)據(jù)統(tǒng)一寫回到磁盤中的數(shù)據(jù)文件。數(shù)據(jù)中的檢查點回周期性的執(zhí)行,以此保證內(nèi)存中的數(shù)據(jù)可以有效的被寫回磁盤。
后臺進程-LGWR
數(shù)據(jù)庫中 DBWR 進程觸發(fā)時,會響應(yīng)啟動另一個后臺進程-LGWR,用于將數(shù)據(jù)庫中數(shù)據(jù)變更的歷史記錄到日志文件中。并且,DBWR 后臺進程在 LGWR 進程寫入重做日志期間會保持阻塞狀態(tài),當(dāng) LGWR 寫入日志完成后,DBWR 開始將內(nèi)存中的數(shù)據(jù)寫入到磁盤中。
在 SGA 內(nèi)存區(qū)中,保存重做日志的區(qū)域稱為 Redo Log Buffer(重做日志緩存區(qū)),當(dāng)需要將數(shù)據(jù)修改對應(yīng)的日志記錄到日志文件中時,會觸發(fā) LGWR 后臺進程將緩存區(qū)的日志寫入到重做日志文件中。
在Oracle數(shù)據(jù)庫中,日志的完整性是十分重要的。絕不僅僅只是記錄,當(dāng)實例異常故障后再恢復(fù)時需要應(yīng)用重做日志文件,將數(shù)據(jù)庫恢復(fù)到故障前一刻的狀態(tài)。只有被寫入重做日志中的數(shù)據(jù),在內(nèi)存區(qū)域被修改的數(shù)據(jù)才算是安全的。
與數(shù)據(jù)何時寫回到磁盤一樣,數(shù)據(jù)庫日志緩存區(qū)的日志也需要根據(jù)一定的規(guī)則將日志文件記錄到重做日志文件中。觸發(fā) LGWR 進程寫入日志的情況包含以下幾種:
當(dāng)用戶進程執(zhí)行了 rollback(回滾) 或 commit(提交) 事件時,數(shù)據(jù)庫會啟動后臺的 LGWR 進程,將相應(yīng)的數(shù)據(jù)變更的記錄記錄到聯(lián)機重做日志文件中;同樣,當(dāng)內(nèi)存區(qū)域的日志緩存區(qū)的空間不夠用時,也會觸發(fā) LGWR 后臺進程將日志緩存區(qū)的日志寫入到重做日志中。
與 DBWR 一樣,LGWR 也會每隔3s將日志緩存區(qū)的日志寫入到重做日志中。
redo log buffer 內(nèi)存區(qū)域的日志占用的空間達到一定限制后(默認超過日志緩存區(qū)1/3的空間時),觸發(fā) LGWR 后臺進程開始將緩存區(qū)的日志寫入到重做日志中。
最后,LGWR 寫日志時會根據(jù)日志出現(xiàn)的先后順序?qū)?nèi)存區(qū)域的日志順序的寫入到日志文件中,是為了數(shù)據(jù)庫恢復(fù)時按照數(shù)據(jù)被修改的先后順序應(yīng)用redo,保證數(shù)據(jù)的正確性和一致性。