oracle深度解析檢查點
由于中LGWR和DBWR工作的不一致,Oracle引入了檢查點的概念,用于同步,保證數據庫的一致性。在Oracle里面,檢查點分為兩種:完全檢查點和增量檢查點。下面我們分別介紹這兩種檢查點的作用:
1、完全檢查點
在Oracle8i之前,數據庫的發生的檢查點都是完全檢查點,完全檢查點會將數據緩沖區里面所有的臟數據塊寫入相應的數據文件中,并且同步數據文件頭和控制文件,保證數據庫的一致。完全檢查點在8i之后只有在下列兩種情況下才會發生:
(1)DBA手工執行alter system checkpoint的命令;
(2)數據庫正常shutdown(immediate,transcational,normal)。
由于完全檢查點會將所有的臟數據庫塊寫入,巨大的IO往往會影響到數據庫的性能。因此Oracle從8i開始引入了增量檢查點的概念。
2、增量檢查點
Oracle 從8i開始引入了檢查點隊列這么一種概念,用于記錄數據庫里面當前所有的臟數據塊的信息,DBWR 根據這個隊列而將臟數據塊寫入到數據文件中。檢查點隊列按時間先后記錄著數據庫里面臟數據塊的信息,里面的條目包含RBA(Redo Block Address,重做日志里面用于標識檢查點期間數據塊在重做日志里面第一次發生更改的編號)和數據塊的數據文件號和塊號。在檢查點期間不論數據塊更改幾次,它在檢查點隊列里面的位置始終保持不變,檢查點隊列也只會記錄它最早的RBA,從而保證最早更改的數據塊能夠盡快寫入。當DBWR將檢查點隊列里面的臟數據塊寫入到數據文件后,檢查點的位置也要相應地往后移,CKPT每三秒會在控制文件中記錄檢查點的位置,以表示Instance Recovery時開始恢復的日志條目,這個概念稱為檢查點的“心跳”(heartbeat)。檢查點位置發生變更后,Oracle里面通過4個參數用于控制檢查點位置和最后的重做日志條目之間的距離。在這里面需要指出的是,多數人會將這4個參數看作控制增量檢查點發生的時間。事實上這是錯誤的,這4個參數是用于控制檢查點隊列里面的條目數量,而不是控制檢查點的發生。
(1)fast_start_io_target
該參數用于表示數據庫發生Instance Recovery的時候需要產生的IO總數,它通過v$filestat的AVGIOTIM來估算的。比如我們一個數據庫在發生Instance Crash后需要在10分鐘內恢復完畢,假定OS的IO每秒為500個,那么這個數據庫發生Instance Recovery的時候大概將產生500*10*60=30,000次IO,也就是我們將可以把fast_start_io_target設置為 30000。
(2)fast_start_mttr_target
我們從上面可以看到fast_start_io_target 來估算檢查點位置比較麻煩。Oracle為了簡化這個概念,從9i開始引入了 fast_start_mttr_target這么一個參數,用于表示數據庫發生Instance Recovery的時間,以秒為單位。這個參數我們從字面上也比較好理解,其中的mttr是mean time to recovery的簡寫,如上例中的情況我們可以將fast_start_mttr_target設置為600。當設置了 fast_start_mttr_target后,fast_start_io_target這個參數將不再生效,從9i后 fast_start_io_target這個參數被Oracle廢除了。
(3)log_checkpoint_timeout
該參數用于表示檢查點位置和重做日志文件末尾之間的時間間隔,以秒為單位,默認情況下是1800秒。
(4)log_checkpoint_interval
該參數是表示檢查點位置和重做日志末尾的重做日志塊的數量,以OS塊表示。
(5)90% OF SMALLEST REDO LOG
除了以上4個初始化參數外,Oracle內部事實上還將重做日志文件末尾前面90%的位置設為檢查點位置。在每個重做日志中,這么幾個參數指定的位置可能不盡相同,Oracle將離日志文件末尾最近的那個位置確認為檢查點位置。
oracle 9i instance recovery
1、增量檢查點
在checkpoint queue的基礎上實現了增量檢查點,每3秒發生一次checkpoint heartbeat,記錄dbwr上次寫成功的最大RBA(redo block address)。這樣的話做instance recovery的時候就從這個rba開始,而不是從上次checkpoint scn開始,大大節省了恢復時間。
2、twice scan of redo log
在應用redo之前,redo將會被操作兩次,第一次去掃描哪些redo record需要被應用,因為9i在redo里添加了dbwr寫數據塊的信息,所以dbwr發生前的日志將不會被應用。第二步就是選出需要被應用的日志然后開始rollforward。
3、rollforward
在做instance recovery時必須先定位到redo log 然后應用所有日志到datafile,這時候包括了committed和uncommitted的數據。當做完rollward,數據庫就可以open了。
4、rollback
因 為rollforward產生了uncommitted數據,所以必須回滾這些數據。這將由smon和on-demand rollback來實現。smon將會掃描undo segment header去標志所有活動事務為dead,然后會逐漸去回滾這些事務。另外on-demand rollback提供了前臺進程進行rollback,當前臺進程企圖獲得被dead事務占用row lock,這時候前臺進程將會去undo segment取得before image去回滾這個塊,至于其他被這個dead事務lock的塊就等待smon去回滾。
另外,如果 在數據庫打開的過程中process crash導致transaction dead,resource不能被釋放的情況,這時候如果另一個進程需要這些resource,那么這個進程將會等待直到pmon清理dead process釋放出resource。
如果數據庫Crash,重新啟動,很久遠以前的未提交事務并不在Redo的恢復序列中。
但是未提交事務一定在回滾段事務表上存在,并且State=10,為活動事務。這就夠了。
數據庫啟動之后,這些事務會被SMON逐個標記為Dead(不可能再活過來了),然后由SMON慢慢去回滾這些事務;也存在另外一種情況,后來的進程會去讀這些未提交數據,發現Dead事務未提交,則主動進行回滾。
1、一個數據塊發生更新,必然寫回滾
2、回滾段的block變化也記錄在redo中
一份未提交的數據必定在回滾中有相應的前鏡像,任何正常的恢復都一定會把這些變化重新構建出來。
想像一下
1、update事務1更新了block 1
2、回滾段1記錄了block1的前鏡像
3、checkpoint
4、update事務2更新了block2
5、回滾段2記錄了block2的前鏡像
6、instance crash
現在重啟數據庫
1、根據redo重新構建block2
2、根據redo重新構建回滾段2
3、database open
4、SMON用回滾段2的數據回滾block2,SMON用回滾段1的數據回滾block1
最后一步也可能是
在另外一個select檢索到block1或者block2的時候,發現這兩個block的數據都是未提交的,此時再回滾block1和block2。
所以,只要有相應的回滾數據存在,無論什么時候oracle都可以找到一致的數據,oracle只需要知道這個事務是提交了的還是沒提交了的,而這點在block header ITL中有記錄。