日志記錄
使用最為廣泛的記錄數(shù)據(jù)庫修改的結(jié)構(gòu)就是日志并炮。日志是日志記錄的序列,它記錄數(shù)據(jù)庫中的所有更新活動(dòng)甥郑。
??日志記錄有幾種逃魄,更新日志記錄(update log record)描述一次數(shù)據(jù)庫寫操作,它具有如下幾個(gè)字段:
- 事務(wù)標(biāo)識(shí):執(zhí)行write操作的事務(wù)的唯一標(biāo)識(shí)
- 數(shù)據(jù)項(xiàng)標(biāo)識(shí):是所寫數(shù)據(jù)項(xiàng)的唯一標(biāo)識(shí)澜搅。通常是數(shù)據(jù)項(xiàng)在磁盤上的位置伍俘,包括數(shù)據(jù)項(xiàng)所駐留的塊的塊標(biāo)識(shí)和塊內(nèi)偏移量
- 舊值:數(shù)據(jù)項(xiàng)的寫前值
- 新值:數(shù)據(jù)項(xiàng)的寫后值
我們將一個(gè)更新日志記錄表示為<Ti,Xj勉躺,V1癌瘾,V2>,表明事務(wù)Ti對(duì)數(shù)據(jù)項(xiàng)Xj執(zhí)行了一個(gè)寫操作饵溅,寫操作前Xj的值時(shí)V1柳弄,寫操作后Xj的值是V2。其他專門的日志記錄用于記錄事務(wù)處理過程中的重要事件概说,如事務(wù)的開始以及事務(wù)的提交或中止。如下是一些日志記錄類型:
- <Ti start>:事務(wù)Ti開始
- <Ti commit>:事務(wù)Ti提交
- <Ti abort>:事務(wù)Ti中止
后面將介紹幾種其他的日志記錄類型嚣伐。
??每次事務(wù)執(zhí)行寫操作時(shí)糖赔,必須在數(shù)據(jù)庫修改前建立建立該次寫操作的日志記錄并把它加入到日志中。一旦日志記錄已存在轩端,就可以根據(jù)需要將修改輸出到數(shù)據(jù)庫中放典。并且,我們有能力撤銷已經(jīng)輸出到數(shù)據(jù)庫中的修改,這是利用日志記錄中的舊值字段來做的奋构。
??為了從系統(tǒng)故障和磁盤故障中恢復(fù)時(shí)能使用日志記錄壳影,日志必須存放在穩(wěn)定存儲(chǔ)器中。
數(shù)據(jù)庫修改
事務(wù)在進(jìn)行數(shù)據(jù)項(xiàng)修改中所采取的步驟:
??1.事務(wù)在主存中自己私有的部分執(zhí)行某些計(jì)算弥臼。
??2.事務(wù)修改主存的磁盤緩沖區(qū)中包含該數(shù)據(jù)項(xiàng)的數(shù)據(jù)塊宴咧。
??3.數(shù)據(jù)庫系統(tǒng)執(zhí)行output操作,將數(shù)據(jù)塊寫入磁盤中径缅。
如果一個(gè)事務(wù)執(zhí)行了對(duì)磁盤緩沖區(qū)或磁盤自身的更新掺栅,我們說這個(gè)事務(wù)修改了數(shù)據(jù)庫;而對(duì)事務(wù)在主存中自己私有的部分進(jìn)行的更新不算數(shù)據(jù)庫修改纳猪。如果一個(gè)事務(wù)直到它提交時(shí)都沒有修改數(shù)據(jù)庫氧卧,我們就說它采用了延遲修改技術(shù)。如果數(shù)據(jù)庫修改在事務(wù)仍然活躍時(shí)發(fā)生氏堤,我們就說它采用了立即修改技術(shù)沙绝。
恢復(fù)算法必須考慮多種因素,包括:
- 有可能一個(gè)事務(wù)已經(jīng)提交了鼠锈,雖然它所做的某些數(shù)據(jù)庫修改還僅僅存在于主存的磁盤緩沖區(qū)中闪檬,而不再磁盤的數(shù)據(jù)庫中。
- 有可能處于活動(dòng)狀態(tài)的一個(gè)事務(wù)已經(jīng)修改了數(shù)據(jù)庫脚祟,而作為后來發(fā)生的故障的結(jié)果谬以,這個(gè)事務(wù)需要終止。
由于所有的數(shù)據(jù)庫修改之前必須建立日志記錄由桌,因此系統(tǒng)有數(shù)據(jù)庫修改前的舊值和要寫給數(shù)據(jù)項(xiàng)的新值可以用为黎。這就使得系統(tǒng)能執(zhí)行適當(dāng)?shù)膗ndo和redo操作。
- undo使用一個(gè)日志記錄行您,將該日志記錄中指明的數(shù)據(jù)項(xiàng)設(shè)置為舊值
- redo使用一個(gè)日志記錄铭乾,將該日志記錄中指明的數(shù)據(jù)項(xiàng)設(shè)置為新值
檢查點(diǎn)和日志的活動(dòng)部分
當(dāng)系統(tǒng)故障發(fā)生時(shí),我們必須檢查日志娃循,決定哪些事務(wù)需要重做炕檩,哪些需要撤銷年栓。原則上稍途,我們需要搜索整個(gè)日志來確定該信息章钾。這樣做有兩個(gè)主要的困難:
- 搜索過程太耗時(shí)
- 根據(jù)我們的算法尝丐,大多數(shù)需要重做的事務(wù)已經(jīng)把其更新寫入數(shù)據(jù)庫洲拇。盡管對(duì)它們重做不會(huì)造成不良后果福荸,但會(huì)使恢復(fù)過程變得更長拭宁。
為了降低這種開銷跳昼,引入檢查點(diǎn)姓迅。
檢查點(diǎn)操作
SQL Server 生成檢查點(diǎn)的過程如下:
①將標(biāo)記檢查點(diǎn)起點(diǎn)的日志記錄寫入日志文件敲霍。(日志記錄≠日志文件)
②將為檢查點(diǎn)記錄的信息存儲(chǔ)在檢查點(diǎn)日志記錄鏈中俊马。
??檢查點(diǎn)中記錄的一條信息是“最小恢復(fù) LSN”(“MinLSN”),它是成功進(jìn)行數(shù)據(jù)庫范圍內(nèi)回滾所需的最早日志記錄的日志序列號(hào)肩杈。MinLSN 是下列各項(xiàng)中的最小者:檢查點(diǎn)起點(diǎn)的 LSN柴我。
最早的活動(dòng)事務(wù)起點(diǎn)的 LSN。
尚未傳遞給分發(fā)數(shù)據(jù)庫的最早的復(fù)制事務(wù)起點(diǎn)的 LSN扩然。
③在檢查點(diǎn)記錄中記錄所有的未完成的活動(dòng)事務(wù)艘儒。
④如果數(shù)據(jù)庫工作在【簡單恢復(fù)模式】,刪除新的MinLSN之前的所有日志記錄(日志截?cái)啵?/p>
⑤將當(dāng)前位于主存的所有日志記錄輸出到穩(wěn)定存儲(chǔ)區(qū)与学。
⑥將所有修改后的緩沖塊寫入磁盤彤悔。
⑦將標(biāo)記檢查點(diǎn)記錄結(jié)束的記錄寫入日志文件。
⑧將檢查點(diǎn)日志記錄鏈<checkpoint L>輸出到穩(wěn)定存儲(chǔ)器索守,其中L是執(zhí)行檢查點(diǎn)時(shí)正活躍的事務(wù)的列表晕窑。
在檢查點(diǎn)執(zhí)行過程中,不允許事務(wù)執(zhí)行任何更新動(dòng)作卵佛,如往緩沖塊中寫入或?qū)懭罩居涗洝?br>
??在上面的過程中杨赤,我們?cè)谌罩局屑尤肓?strong><checkpoint L>記錄使得系統(tǒng)提高恢復(fù)過程的效率〗赝簦考慮在檢查點(diǎn)前完成的事務(wù)Ti疾牲。對(duì)于這樣的事務(wù),<Ti commit>記錄(或<Ti abort>記錄)在日志中出現(xiàn)在<checkpoint>記錄之前衙解。Ti所做的任何數(shù)據(jù)庫修改都必然已在檢查點(diǎn)前或作為檢查點(diǎn)本身的一部分寫入數(shù)據(jù)庫阳柔。因此,在恢復(fù)時(shí)就不必再對(duì)Ti執(zhí)行redo操作了蚓峦。
??在系統(tǒng)崩潰發(fā)生之后舌剂,系統(tǒng)檢查日志以找到最后一條<checkpoint L>記錄(這可以通過從尾端開始反向搜索日志來進(jìn)行,直到遇到第一條<checkpoint L>記錄)
??只需要對(duì)L中的事務(wù)暑椰,以及<checkpoint L>記錄寫到日志中之后才執(zhí)行的事務(wù)進(jìn)行undo或redo操作霍转。讓我們把這個(gè)事務(wù)集合記為T。
- 對(duì)T中所有事務(wù)Tk一汽,若日志中既沒有<Tk commit>記錄避消,也沒有<Tk abort>記錄,則執(zhí)行undo(Tk)召夹。
- 對(duì)T中所有事務(wù)Tk岩喷,若日志中有<Tk commit>記錄或<Tk abort>記錄,則執(zhí)行redo(Tk)监憎。
請(qǐng)注意均驶,要找出事務(wù)集合T,和確定T中的每個(gè)事務(wù)是否有commit或abort記錄出現(xiàn)在日志中枫虏,我們只需要檢查日志中從最后一條checkpoint日志記錄開始的部分妇穴。
MinLSN的選擇
??MinLSN實(shí)際上代表了數(shù)據(jù)庫從檢查點(diǎn)恢復(fù)時(shí),具體從哪個(gè)LSN號(hào)開始掃描并進(jìn)行恢復(fù)隶债。所以MinLSN的選擇是檢查點(diǎn)時(shí)的LSN和最早的活動(dòng)事務(wù)起點(diǎn)LSN兩者比較的最小值腾它。
??如圖,事務(wù)2為檢查點(diǎn)時(shí)的唯一活動(dòng)事務(wù)死讹,其起點(diǎn)的LSN1小于檢查點(diǎn)發(fā)生時(shí)的LSN2瞒滴,所以MinLSN取LSN1就可以。
檢查點(diǎn)與恢復(fù)效率的關(guān)系
??檢查點(diǎn)將臟數(shù)據(jù)頁從當(dāng)前數(shù)據(jù)庫的緩沖區(qū)高速緩存刷新到磁盤上赞警。 這最大限度地減少了恢復(fù)時(shí)必須重做(Redo)的修改量妓忍。
??為什么在日志文件中設(shè)置了檢查點(diǎn)之后,基于日志的恢復(fù)機(jī)制就可以提高效率了呢愧旦?如圖所示為檢查點(diǎn)發(fā)生時(shí)可能的事務(wù)的狀態(tài)世剖。
① 事務(wù)1
其start和commit日志記錄都發(fā)生在檢查點(diǎn)之前,這樣的事務(wù)其結(jié)果已經(jīng)反映到物理介質(zhì)上去了(因?yàn)闄z查點(diǎn)會(huì)保證WAL協(xié)議笤虫,確保數(shù)據(jù)被寫入)旁瘫,所以在恢復(fù)時(shí)無須對(duì)該事務(wù)做Redo操作。
② 事務(wù)2
其start日志記錄在檢查點(diǎn)之前發(fā)生琼蚯,其commit記錄在故障點(diǎn)之前發(fā)生酬凳,說明日志中事務(wù)已經(jīng)完美提交,但數(shù)據(jù)不一定已經(jīng)寫入遭庶,所以屬于圓滿事務(wù)宁仔,需要Redo操作。
③ 事務(wù)3
其start日志記錄在檢查點(diǎn)之后發(fā)生峦睡,其commit記錄在故障點(diǎn)之前發(fā)生翎苫,說明日志中事務(wù)已經(jīng)完美提交,但數(shù)據(jù)不一定已經(jīng)寫入赐俗,所以屬于圓滿事務(wù)拉队,需要Redo操作。
④ 事務(wù)4
其start日志記錄在檢查點(diǎn)之后發(fā)生阻逮,其commit記錄在故障點(diǎn)之前尚未發(fā)生粱快,說明日志中事務(wù)為中止事務(wù),需要Undo操作叔扼。
⑤ 事務(wù)5
其start日志記錄在檢查點(diǎn)之前發(fā)生事哭,其commit記錄在故障點(diǎn)之前尚未發(fā)生,說明日志中事務(wù)為中止事務(wù)瓜富,需要Undo操作鳍咱。
由CheckPoint的機(jī)制可以看出,由于內(nèi)存中的數(shù)據(jù)往往比持久化存儲(chǔ)中的數(shù)據(jù)更新与柑,而CheckPoint保證了這部分?jǐn)?shù)據(jù)能夠被持久化到磁盤谤辜,因此CheckPoint之前的數(shù)據(jù)一定不會(huì)再需要被Redo蓄坏。
自動(dòng)檢查點(diǎn)
?? SQL Server 會(huì)自動(dòng)產(chǎn)生檢查點(diǎn)事件,它在后臺(tái)有一條線程負(fù)責(zé)產(chǎn)生與執(zhí)行檢查點(diǎn)事件丑念。 自動(dòng)檢查點(diǎn)之間的間隔基于使用的日志空間量以及自上一個(gè)檢查點(diǎn)以來經(jīng)歷的時(shí)間涡戳。 如果只在數(shù)據(jù)庫中進(jìn)行了很少的修改,自動(dòng)檢查點(diǎn)之間的時(shí)間間隔可能變化很大并且很長脯倚。 如果修改了大量數(shù)據(jù)渔彰,自動(dòng)檢查點(diǎn)也會(huì)經(jīng)常出現(xiàn)。
??自動(dòng)檢查點(diǎn)之間的間隔是以“恢復(fù)間隔”配置算出的推正。此選項(xiàng)指定數(shù)據(jù)庫引擎在系統(tǒng)重新啟動(dòng)時(shí)恢復(fù)數(shù)據(jù)庫所用的最長時(shí)間恍涂。 數(shù)據(jù)庫引擎將估計(jì)在執(zhí)行恢復(fù)操作期間自己在“恢復(fù)間隔”內(nèi)能夠處理多少條日志記錄。
??自動(dòng)檢查點(diǎn)之間的間隔也取決于恢復(fù)模式:
- 如果數(shù)據(jù)庫使用的是完整恢復(fù)模式或批量日志恢復(fù)模式植榕,則每當(dāng)日志記錄數(shù)達(dá)到數(shù)據(jù)庫引擎估計(jì)在“恢復(fù)間隔”選項(xiàng)中指定的時(shí)間內(nèi)可以處理的數(shù)量時(shí)再沧,便會(huì)生成一個(gè)自動(dòng)檢查點(diǎn)。
- 如果數(shù)據(jù)庫使用的是簡單恢復(fù)模式内贮,只要日志記錄數(shù)達(dá)到下面兩個(gè)值中較小的那個(gè)值产园,就會(huì)生成自動(dòng)檢查點(diǎn):
- 日志已滿 70%。
- 日志記錄數(shù)達(dá)到數(shù)據(jù)庫引擎估計(jì)在“恢復(fù)間隔”選項(xiàng)指定的時(shí)間內(nèi)能夠處理的記錄數(shù)夜郁。