聊聊數(shù)據(jù)庫的IO丟失問題
昨天晚上一個朋友打電話咨詢一個Oracle數(shù)據(jù)庫無法啟動的問題,是因為之前出現(xiàn)的異常宕機引發(fā)的。這是一個因為數(shù)據(jù)庫IO丟失引發(fā)的數(shù)據(jù)庫不一致問題,Oracle在數(shù)據(jù)庫啟動的 時候發(fā)現(xiàn)了一些比較嚴重的不一致問題,就會無法打開數(shù)據(jù)庫。在數(shù)據(jù)庫原理中,數(shù)據(jù)庫通過Write Ahead Log(WAL)機制來確保數(shù)據(jù)庫在實例宕機之后不會出現(xiàn)數(shù)據(jù)的不一致問題,不會丟失已經(jīng)提交過的事務。從基礎原理上,似乎數(shù)據(jù)庫能夠自己保證自己的一致性,為什么還會出現(xiàn)類似的問題呢?
實際上這種設計是有一個前提的,那就是每個已經(jīng)寫盤的IO,都真實地物理落盤了。這是數(shù)據(jù)庫的數(shù)據(jù)永遠保持一致性的前提,如果這個前提出現(xiàn)了漏洞,那么這個理論上的永遠一致性就不存在了。
事實上,數(shù)據(jù)庫的IO鏈路很長,操作系統(tǒng)、RAID卡、HBA卡、SAN交換機緩沖區(qū)、集中式存儲機頭、集中式存儲的寫緩沖、磁盤的寫緩沖、磁盤,等等。我可能還沒有羅列完整,因為在不同的環(huán)境中,這個鏈路還會略微不同。在很多層面,IO都會被優(yōu)化,因此真實的IO落盤并非和我們想象的一樣。如果在云環(huán)境中,這個IO路徑就更為復雜了。
當IO在這些環(huán)節(jié)中的某個緩沖中丟失了,那么數(shù)據(jù)庫的底層就會丟失IO了,這個IO丟失會引發(fā)一系列的數(shù)據(jù)不一致。比如一個8K的數(shù)據(jù)塊,前半部分已經(jīng)寫盤,但是后半部分的寫IO丟失了,這樣就會出現(xiàn)“塊斷裂”,一個數(shù)據(jù)塊的數(shù)據(jù)不一致了。
MySQL等數(shù)據(jù)庫使用DOUBLE WRITE BUFFER來解決這個問題,PG則采用FULL PAGE WRITE LOG的機制。Oracle則比較粗獷,完全不管這個問題,讓底層存儲系統(tǒng)來確保寫IO的原子性。究其原因,Oracle自從出生開始,就是和高端硬件關聯(lián)的,和MySQL/PG這些草根的設計思路完全不同。
數(shù)據(jù)庫面對復雜的底層環(huán)境,所以無法確保其基礎理論的實現(xiàn),這個可能會出乎一些朋友的意料。Michael Stonebraker老爺子要搞DBOS,其目的是為數(shù)據(jù)庫提供一個完全以數(shù)據(jù)庫的設計理念為基礎的底層環(huán)境。這個理想很宏大,實現(xiàn)起來恐怕也是困難重重的。因為這些違背數(shù)據(jù)庫設計理念的設計都是為了優(yōu)化。一個通用的DBOS可能無法適應不同的數(shù)據(jù)庫產(chǎn)品的通用優(yōu)化需求。DBOS作為一個數(shù)據(jù)庫SAAS服務的基礎平臺是可行的,成為一個通用的數(shù)據(jù)庫底座任重道遠。
當?shù)讓覫O出現(xiàn)丟失的時候,Oracle處置起來是最為麻煩的,我想很多Oracle的老DBA也都因此賺了不少錢,昨晚我那個朋友就因為幫人打開了一個出現(xiàn)ORA-600[2662]的數(shù)據(jù)庫而賺了1萬塊錢的外快。這是因為Oracle的控制文件、REDO、UNDO、數(shù)據(jù)文件一旦因為IO丟失而出現(xiàn)不一致會引發(fā)數(shù)據(jù)庫無法打開的問題。
ORA-01113、ORA-600[2662]、ORA-600[3020]、ORA-600 [4000]、ORA-600[4193]、ORA-600[4194]等錯誤的出現(xiàn)往往就與這些有關。20年前我在ORACLEFANS網(wǎng)站上也發(fā)過不少處置類似問題的文章,在微信公眾號里我也寫過一篇《如何強制打開無法啟動的Oracle數(shù)據(jù)庫》,里面簡單地介紹了一些處理方法。
如果出現(xiàn)ORA-600[2662],整個處理過程會麻煩一些,因為數(shù)據(jù)庫啟動的時候發(fā)現(xiàn)某個數(shù)據(jù)文件的SCN已經(jīng)高于數(shù)據(jù)庫的當前SCN。早期我們可以通過adjust_scn事件來往前推進數(shù)據(jù)庫的當前SCN,從而解決這個問題。Oracle 11.2.0.2.6以后,adjust_scn等待事件被廢棄了,如果遇到這種情況,用oradebug 修改內(nèi)存中的CURRENT SCN也可以起作用。從Oracle 12c開始廢棄了這種做法,Oracle又提供了一個EVENT 21307096來解決這個問題(詳情請參考《Force Open Database after applying Patch 21307096 (Doc ID 2674196.1)》)。
實際上Oracle數(shù)據(jù)庫IO丟失在SYSTEM表空間中才是最麻煩的,因為在SYSTEM表空間中存在bootstrap objects,還有一個system rollback segment。
如果這些對象丟失了IO,那么就需要使用BBED這樣的工具去修復才能避開問題,強行打開數(shù)據(jù)庫。強行打開了丟失IO的數(shù)據(jù)庫之后,大部分數(shù)據(jù)可以順利導出,如果某些表上還是存在壞塊,需要通過跳過壞塊的技術來導出表中的數(shù)據(jù)。一般來說這個數(shù)據(jù)庫已經(jīng)不能作為生產(chǎn)庫使用了,導出數(shù)據(jù)后重建數(shù)據(jù)庫是最好的做法。
一般來說這些處置手段都是作為最后的處置手段,如果數(shù)據(jù)庫存在備份,而且備份中丟失的數(shù)據(jù)在合理的范圍內(nèi),通過備份恢復數(shù)據(jù)庫可能是更好的做法。遇到這樣問題的客戶往往是比較慷慨的,因此DBA掌握這些技術,還是有價值的。