簡介
大同ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為成都創(chuàng)新互聯(lián)公司的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書合作)期待與您的合作!
保證數(shù)據(jù)的一致性是數(shù)據(jù)庫的一個最最基本的功能,那數(shù)據(jù)庫在機(jī)器down機(jī)或者出現(xiàn)其他意外的情況下是如何去保證數(shù)據(jù)庫的數(shù)據(jù)的一致性的呢?數(shù)據(jù)庫本身主要依靠undolog和redolog兩種日志文件去保持?jǐn)?shù)據(jù)的一致性,本文將圍繞undolog進(jìn)行介紹。如何利用undolog去實(shí)現(xiàn)數(shù)據(jù)庫的一致性。
數(shù)據(jù)庫架構(gòu)簡介
要介紹數(shù)據(jù)庫一致性的實(shí)現(xiàn)機(jī)制,自然少不了要介紹下數(shù)據(jù)庫的整體架構(gòu),這里畫一個簡圖來介紹下數(shù)據(jù)庫的架構(gòu)。(因?yàn)閿?shù)據(jù)庫的架構(gòu)不是本文重點(diǎn),所以這里就畫個簡圖,想要詳細(xì)了解數(shù)據(jù)庫架構(gòu)的,可以再查閱其他文章。)
將數(shù)據(jù)庫簡化,主要分這么幾塊:
§ 查詢處理器,主要負(fù)責(zé)針對于查詢sql的解析、執(zhí)行計(jì)劃的選擇等等。
§ 事務(wù)管理器,事務(wù)是數(shù)據(jù)庫操作的最小單位,事務(wù)管理器主要針對于分配事務(wù)id等等管理工作
§ 日志管理器
§ 恢復(fù)管理器
§ 緩沖管理器,這個大家也清楚,數(shù)據(jù)庫中的寫操作都是在緩沖器中完成,然后再flush到硬盤上。
§ 硬盤數(shù)據(jù),日志無論是數(shù)據(jù)庫的數(shù)據(jù)、還是日志文件,都是最終要寫到硬盤上做持久化存儲的。
下面就針對于上面說的這幾塊組成部分,來描述下數(shù)據(jù)庫是如何做災(zāi)難恢復(fù)的。這一篇主要還是講undolog,下一篇再講redolog。
undo日志簡介
undo日志,顧名思義,就是撤銷日志,也就是說,這個日志里面記錄了相關(guān)的撤銷操作。通過剛才的數(shù)據(jù)庫架構(gòu)簡圖,我們也可以看到,針對于寫數(shù)據(jù)等處理主要還是在內(nèi)存中進(jìn)行,既然在內(nèi)存中進(jìn)行,那就會出現(xiàn)由于機(jī)器宕機(jī)導(dǎo)致的丟數(shù)據(jù)問題。那數(shù)據(jù)庫是如何通過undo日志去保證數(shù)據(jù)的一致性的呢?
要描述這個問題,我們需要先定義幾種操作。假設(shè)我們現(xiàn)在要做這樣一件事情,需要把某條數(shù)據(jù)X從數(shù)據(jù)庫中讀出來,然后再去改變他的值,改為Y,然后再寫回去。好,這樣一個操作,數(shù)據(jù)庫可能要走這樣幾個流程,首先會看緩沖區(qū)里有沒有,假設(shè)有則直接將數(shù)據(jù)返回,我們稱這樣一個過程為Read(X),假設(shè)緩沖區(qū)里沒有,那就需要先從硬盤讀到緩沖區(qū),然后再返回給用戶,那我們定義從硬盤讀到緩沖區(qū)的過程為Input(X),也就是說,如果緩沖區(qū)里沒有,那數(shù)據(jù)庫要先經(jīng)過一個Read(X),然后再經(jīng)過一個Input(X),然后再經(jīng)過一個Read(X)。那修改時也一樣,數(shù)據(jù)庫要修改緩沖區(qū)的內(nèi)容,那這個操作我們成為Write(Y).還要經(jīng)過一個從內(nèi)存刷到磁盤的過程,這個過程我們成為output(Y)。好,有了這幾個定義,我們就挨個從這幾個過程去分析,如果數(shù)據(jù)庫再這中間某個過程掛了,如何去保證數(shù)據(jù)的一致性。
在介紹前,先簡要說下undolog日志的格式,undolog日志的格式是這樣的
那比如我們要做上面那個問題,即讀取一個值再修改(假設(shè)不在緩沖區(qū)中),那就要經(jīng)過以下幾步:
見表格:
編號 | 操作 | undolog |
1 | – | start |
2 | Read(X) | – |
3 | Input(X) | – |
4 | Read(X) | – |
5 | Write(Y) | – |
6 | – | |
7 | flush undolog | |
8 | Output(Y) | – |
9 | … | … |
10 | commit | – |
11 | – | end |
10 | flush undolog |
我們看上面這個表格,下面我就解釋下這個表格。首先,我們需要明確的一點(diǎn)是,無論是操作數(shù)據(jù)庫中的數(shù)據(jù),還是日志,都是先在內(nèi)存中操作,然后flush到硬盤上。這一點(diǎn)是毫無疑問的。
前4步應(yīng)該都容易理解,一開始在undolog中需要記錄一個start的標(biāo)志位,然后2、3、4步都是讀取數(shù)據(jù)庫的內(nèi)容,第5步往內(nèi)存中寫,將X的值改為Y,然后第6步undolog中會記錄下事務(wù)T中A的原先值是X,那到了第7步呢?是到底是應(yīng)該先將undolog進(jìn)行flush,還是應(yīng)該output后再flush呢?
我們做個假設(shè),假設(shè)output后再進(jìn)行l(wèi)og的flush,如果恰好就在output后,數(shù)據(jù)庫down機(jī)了,那這樣的結(jié)果顯而易見,日志文件里并沒有記載undolog(因?yàn)闆]有flush到硬盤),無法重做所以就會導(dǎo)致數(shù)據(jù)不一致。所以要在output后進(jìn)行undolog的flush是不可取的。
那我們看下上面這個順序的意義,假設(shè)在第6-7步之間宕機(jī)了,也就就是在還未flush undolog時已宕機(jī),那不會影響數(shù)據(jù)的一致性,因?yàn)楸緛頂?shù)據(jù)就沒有寫到硬盤。如果是在第7-8步間宕機(jī),雖然數(shù)據(jù)庫的數(shù)據(jù)沒有寫到硬盤,但log已經(jīng)flush了,那這時會通過flush后的log重做一遍,因?yàn)橄到y(tǒng)不知道這個log做過了沒有,不過即使是重做一遍也不影響最終的數(shù)據(jù)一致性,也只是將原先的數(shù)據(jù)又重新寫了一遍而已,就是從X寫為X,這個不影響數(shù)據(jù)庫一致性,undolog是冪等的,也就是做幾次的結(jié)果都是一樣。所以上面這個順序才是合理的。
通過undolog進(jìn)行數(shù)據(jù)恢復(fù)
既然有了undolog,那我們看下數(shù)據(jù)庫是如何通過undolog進(jìn)行數(shù)據(jù)恢復(fù)的。這時上面架構(gòu)簡圖里的恢復(fù)管理器就起了作用了,恢復(fù)管理器會掃描undolog,找出沒有end掉的start,因?yàn)閺纳厦娴捻樞蛑形覀兛梢钥闯?,“end”記錄flush到log中是在事務(wù)提交后才flush的,所以,只要是有了end記錄了,就說明了這個事務(wù)本身已經(jīng)結(jié)束了,數(shù)據(jù)一致性已經(jīng)可以保證了。所以恢復(fù)管理器這時是掃描沒有end配位的start,然后從start開始往后,根據(jù)undolog中記錄的先前的值進(jìn)行重做。不過根據(jù)我們剛才這個模型會發(fā)現(xiàn),在恢復(fù)管理器進(jìn)行重做時,是不能有其他的寫入的,也就是現(xiàn)在的寫入應(yīng)該是夯住狀態(tài)的。而且還有個問題,恢復(fù)管理器需要從頭開始對于undolog進(jìn)行掃描,其實(shí)這是不必須的,完全可以有個checkpoint(代表在這之前的數(shù)據(jù)已經(jīng)可以保證數(shù)據(jù)一致性了)的,那恢復(fù)管理器只需要找到最后一個checkpoint,然后從checkpoint往后做就可以了。
針對于以上這兩個問題,我們下面進(jìn)行討論。
在上面介紹的模型中,恢復(fù)管理器必須要通過全篇掃描整個undolog進(jìn)行日志恢復(fù),這樣做顯然是沒有太大必要的,因?yàn)橄到y(tǒng)中斷肯定是在最后幾個事務(wù)受到影響,前面的事務(wù)應(yīng)該已經(jīng)完成commit或者rollback了,不會出現(xiàn)abort的情況,那我們?nèi)绾沃滥男┦聞?wù)受到了影響呢,如果我們知道了哪一些事務(wù)受到了影響,那我們就可以不用全篇進(jìn)行掃描,而僅僅掃描很小的一部分就可以了。下面就介紹下,數(shù)據(jù)庫如何知道哪些事務(wù)受到了影響,數(shù)據(jù)庫為了得到這個目的,引入了檢查點(diǎn)(checkpoint)這個概念。
checkpoint,即檢查點(diǎn)。在undolog中寫入檢查點(diǎn),表示在checkpoint前的事務(wù)都已經(jīng)完成commit或者rollback了,也就是檢查點(diǎn)前面的事務(wù)已經(jīng)不存在數(shù)據(jù)一致性的問題了。那這個checkpoint如何去實(shí)現(xiàn)呢。其實(shí)實(shí)現(xiàn)的機(jī)制很簡單,就是周期性的往undolog里面寫入。當(dāng)然這個寫入肯定不是隨隨便便的往里寫,在往里寫的時候,肯定要檢查前面的事務(wù)是否完成。
這個時候就會帶來一個問題,因?yàn)閿?shù)據(jù)庫是一直在運(yùn)行的,也就是事務(wù)是在不斷啟動的,同時可能有n個事務(wù)已經(jīng)處于開始狀態(tài)。而在檢查點(diǎn)往里寫的時候,可能又有新的事務(wù)啟動了,如果讓檢查點(diǎn)一直等到?jīng)]有新的事務(wù)啟動而且前面所有的事務(wù)又都提交過了估計(jì)很難,那基本檢查點(diǎn)就不用往里寫了。所以,在這種情況下,只能是在檢查點(diǎn)往里寫的時候,停止接受新事務(wù),等待已啟動的事務(wù)提交完畢,然后檢查點(diǎn)寫入完畢。然后繼續(xù)接受新事務(wù)。類似于這樣:例如,現(xiàn)在有T1 T2兩個事務(wù),則undolog中寫入:
undolog |
start T1 |
start T2 |
這時到了檢查點(diǎn)的周期,要往里寫入檢查點(diǎn)了,就得等到T1,T2全部提交完畢,然后寫入檢查點(diǎn)chkpoint。也就是如果現(xiàn)在有一個T3要開啟,是無法開啟的。系統(tǒng)處于夯住狀態(tài)。寫入完后,開啟T3,日志記錄如下:
undolog |
start T1 |
start T2 |
end T1 |
end T2 |
chkpoint |
start T3 |
這時候,如果系統(tǒng)掛掉了,故障恢復(fù)管理器會從undolog的尾部向前進(jìn)行掃描,掃描到checkpoint后,就不會往前掃描了,因?yàn)榍懊娴氖聞?wù)都已經(jīng)提交過了,不存在數(shù)據(jù)一致性問題。所以只需要從checkpoint開始重做即可。
這樣固然是好,省掉了需要undolog從頭開始掃描的麻煩,但是這樣做的缺點(diǎn)也很明顯,那就是在寫入checkpoint的過程中,系統(tǒng)是出于夯住狀態(tài)的,所有的寫入都要暫停。那能否有一種更好的方法既可以寫入checkpoint又不需要系統(tǒng)暫停呢,必須的,當(dāng)然有,這就是下面要講的非靜態(tài)檢查點(diǎn)。
非靜態(tài)檢查點(diǎn)是相對于靜態(tài)檢查點(diǎn)而來的,上文中所提到的就屬于靜態(tài)檢查點(diǎn),因?yàn)樵跈z查點(diǎn)寫入的同時,系統(tǒng)是不能寫入的。而非靜態(tài)檢查點(diǎn)的引入,就是要解決這個問題。
非靜態(tài)檢查點(diǎn)的策略是在寫入chkpoint的同時,會記錄下當(dāng)前活躍的事務(wù)。比如,當(dāng)前狀態(tài)下,T1和T2都是活躍狀態(tài),那么undolog中會被寫入start checkpoint(T1,T2),這時整體系統(tǒng)仍然是正常寫入的,也就是說在這條log寫入后,仍然可以繼續(xù)開啟其他事務(wù)。當(dāng)T1,T2完成后,會寫入end checkpoint的記錄。例如如下記錄:
undolog |
start T1 |
start T2 |
start checkpoint(T1,T2) |
start T3 |
end T1 |
end T2 |
end chkpoint |
start checkpoint(T3) |
end T3 |
end chkpoint |
上面這個日志記錄就是,在T1,T2開始后,undolog中寫入了start checkpoint(T1,T2)的檢查點(diǎn),而這時仍然是可以接受其他事務(wù)的開始的,這時有了T3事務(wù)的開啟。
通過這種機(jī)制,可以有效避免在檢查點(diǎn)寫入時需要停掉服務(wù)的弊端,但現(xiàn)在問題又來了,這樣寫檢查點(diǎn)固然是好,但恢復(fù)管理器如何通過這樣的undolog去進(jìn)行數(shù)據(jù)恢復(fù)操作呢?因?yàn)?,如果檢查點(diǎn)是靜止的,那找到checkpoint后,就不必再往前找了,而現(xiàn)在不一樣了,因?yàn)檎业絜ndcheckpoint后,前面仍可能有未完成的事務(wù),那這時數(shù)據(jù)恢復(fù)是如何恢復(fù)的呢?
在這種情況下,數(shù)據(jù)庫宕機(jī)后,恢復(fù)管理器仍然會從尾往前進(jìn)行掃描undolog,如果遇到了“end chkpoint”,這時并不代表checkpoint前所有的事務(wù)都已經(jīng)提交了,但我們可以知道,所有未提交的事務(wù)都是在上一個startcheckpoint之后,所以會繼續(xù)往前找,一直找到startcheckpoint,找到startcheckpoint后,比如是startcheckpoint(T1,T2),因?yàn)橄惹耙呀?jīng)找到了endchkpoint,所以T1,T2這兩個事務(wù)已經(jīng)可以保證數(shù)據(jù)一致性了,需要重做的就是在startchecpoint(T1,T2)到end chkpoint間的這一些非T1,T2事務(wù),這些是需要重做的,所以要把這些進(jìn)行重做。
還有另外一種情況,就是恢復(fù)管理器在掃描時,先遇到了startcheckpoint(T1,T2)的日志,在這種情況下,我們首先知道了T1,T2或許是未完成的事務(wù),那這時需要在start checkpoint之后找到是否有某個事務(wù)的end語句,如果有,說明這個事務(wù)是完成了,如果沒有,就說明沒有完成,那就要從checkpoint再往后尋找,找到這個事務(wù)的start,然后從start之后往后重做。說得比較羅嗦,我們上個例子來說明下這種情況。
例如,數(shù)據(jù)庫宕機(jī)后,開始掃描undolog,得到以下片段:
undolog |
start T1 |
start T2 |
start checkpoint(T1,T2) |
start T3 |
end T1 |
這時,恢復(fù)管理器拿到這個片段后進(jìn)行掃描,在遇到end chkpoint前遇到了start checkpoint(T1,T2),這說明了,T1,T2是可能未完成事務(wù)的,而且在這之前還遇到了T3的start,沒有end T3,也沒有任何T3的檢查點(diǎn)的開始,這說明了T3一定是未完成事務(wù)的,所以T3一定是要重做的。先前為什么說T1,T2是可能未完成事務(wù)的呢?因?yàn)橛龅搅藄tart checkpoint(T1,T2),沒有遇到end chkpoint,并不代表T1和T2就一定是未完成的,可能有一個已經(jīng)commit過了,因?yàn)閮蓚€都沒有commit,所以才導(dǎo)致了沒有end chkpoint,所以這時找start下面的日志,發(fā)現(xiàn)了“end T1”,說明了T1的事務(wù)是已經(jīng)完成了的。那只需要找T2的開啟然后開始重做就可以了,然后就通過start checkpoint(T1,T2)再往上找,找到了start T2,然后開始重做T2,也就是這個日志里,T2和T3是需要重做的,然后重做掉。(注:剛才先說了做T3,然后有說了重做T2,并不代表真正的順序就是這樣,實(shí)際上恢復(fù)管理器是先分析出需要重做的事務(wù),然后一塊做掉的。