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