欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

MySQL數(shù)據(jù)庫本地事務(wù)原理解析

 更新時間:2022年01月24日 09:08:15   作者:JosenZHANG  
事務(wù)是數(shù)據(jù)庫系統(tǒng)中的重要概念,了解這一律念是以正確的方式開發(fā)和數(shù)據(jù)庫交互的應(yīng)用程序的前提,今天通過本文給大家介紹MySQL數(shù)據(jù)庫本地事務(wù)原理解析,感興趣的朋友一起看看吧

在經(jīng)典的數(shù)據(jù)庫理論里,本地事務(wù)具備四大特征:

  • 原子性

事務(wù)中的所有操作都是以原子的方式執(zhí)行的,要么全部成功,要么全部失?。?/p>

  • 一致性

事務(wù)執(zhí)行前后,所有的數(shù)據(jù)都應(yīng)該處于一致性狀態(tài)---即要滿足數(shù)據(jù)庫表的一致性約束,也要達(dá)到業(yè)務(wù)一致性(完成了業(yè)務(wù)目標(biāo));

  • 隔離性

并發(fā)執(zhí)行的事務(wù)不應(yīng)該相互干擾;隔離性的強(qiáng)度由隔離級別決定;

  • 持久性

事務(wù)一旦被提交,它添加/修改的數(shù)據(jù)不會隨著系統(tǒng)崩潰而丟失;

在MySQL(InnoDB引擎)中,原子性和持久性是通過Redo Log來實(shí)現(xiàn)的,一致性是通過Undo Log實(shí)現(xiàn)的,而隔離性則是通過鎖和MVCC來實(shí)現(xiàn)的。

ARIES算法

如果需要深入了解數(shù)據(jù)庫本地事務(wù)原理,不得不提到ARIES算法,該算法全稱為Algorithms for Recovery and Isolation Exploiting Semantics(基于語義的恢復(fù)與隔離算法),眾多主流的關(guān)系型數(shù)據(jù)庫都受到該算法的影響。

ARIES算法主要針對使用No Force + Steal的數(shù)據(jù)寫入策略而采用的一種數(shù)據(jù)恢復(fù)方式。

該算法主要基于三個主要的原則:

  • Write-ahead logging

出于性能上的考慮,數(shù)據(jù)的修改都是在內(nèi)存中進(jìn)行,并將這些“修改操作”記錄到日志(Redo Log和Undo Log)中,然后異步將內(nèi)存中的數(shù)據(jù)寫入到磁盤;

  • 通過Redo Log恢復(fù)數(shù)據(jù)

Redo Log用于記錄事務(wù)對數(shù)據(jù)的修改操作,在數(shù)據(jù)庫崩潰恢復(fù)時,ARIES通過Redo Log重放那些還未寫入到數(shù)據(jù)庫磁盤中的數(shù)據(jù)操作,將數(shù)據(jù)恢復(fù)至崩潰前的狀態(tài);

  • 通過Undo Log回滾數(shù)據(jù)

對崩潰前未提交的事務(wù),通過Undo Log進(jìn)行回滾;

Write-ahead logging

每個事務(wù)執(zhí)行時,都是在內(nèi)存中進(jìn)行數(shù)據(jù)的修改,并將這些“修改操作”記錄到日志,然后將內(nèi)存中的數(shù)據(jù)異步寫入到磁盤里;

但日志也并非立刻寫入至磁盤,而是先寫入到Log Buffer,再按照相應(yīng)的參數(shù)配置進(jìn)行磁盤的寫入操作;在寫入至磁盤時,數(shù)據(jù)會先寫入至操作系統(tǒng)內(nèi)核緩沖區(qū)(OS Buffer),然后根據(jù)參數(shù)配置決定對內(nèi)核緩沖區(qū)中的數(shù)據(jù)同步或異步刷盤。

如在InnoDB中,Redo Log的磁盤寫入策略是由innodb_flush_log_at_trx_commit參數(shù)值來決定的:

0: 當(dāng)參數(shù)值設(shè)置為0時,每隔1秒將Redo Log Buffer中的數(shù)據(jù)寫入至OS Buffer,并同時調(diào)用fsync()函數(shù)完成刷盤操作;

1: 每次事務(wù)提交時,立即將Redo Log Buffer中的數(shù)據(jù)寫入至OS Buffer,并同時調(diào)用fsync()函數(shù)完成刷盤操作;

2: 每次事務(wù)提交時,立即將Redo Log Buffer中的數(shù)據(jù)寫入至OS Buffer,每隔1秒調(diào)用fsync()函數(shù)完成刷盤操作;

由此可見,當(dāng)innodb_flush_log_at_trx_commit設(shè)置為0或2時,都會導(dǎo)致日志數(shù)據(jù)丟失;

以上討論了“數(shù)據(jù)操作”日志的寫入方式,而對于事務(wù)中真正修改的數(shù)據(jù),Write-ahead logging根據(jù)事務(wù)提交的時間節(jié)點(diǎn),將變動的數(shù)據(jù)寫入至磁盤的時間節(jié)點(diǎn)分為Force和Steal兩種:

Force:在事務(wù)提交時,是否強(qiáng)制將變動的數(shù)據(jù)完全寫入至磁盤?

Steal:在事務(wù)提交前,是否允許將變動的數(shù)據(jù)提前寫入至磁盤?

因此根據(jù)Force和Steal的值,數(shù)據(jù)的寫入策略可以分為以下四種:

StealNo Steal
Force

事務(wù)提交時,強(qiáng)制將變動數(shù)據(jù)完全寫入至磁盤

事務(wù)提交前,允許將變動的數(shù)據(jù)提前寫入至磁盤

事務(wù)提交時,強(qiáng)制將變動數(shù)據(jù)完全寫入至磁盤

事務(wù)提交前,不允許變動的數(shù)據(jù)提前寫入至磁盤

No Force

事務(wù)提交時,不需要強(qiáng)制變動數(shù)據(jù)完全寫入至磁盤

事務(wù)提交前,允許將變動的數(shù)據(jù)提前寫入至磁盤

事務(wù)提交時,不需要強(qiáng)制變動數(shù)據(jù)完全寫入至磁盤

事務(wù)提交前,不允許變動的數(shù)據(jù)提前寫入至磁盤

直觀感覺就可以知道,采用No Force + Steal的方式,不需要在事務(wù)提交時,強(qiáng)制將所有的變動數(shù)據(jù)寫入至磁盤,同時允許變動的數(shù)據(jù)在事務(wù)提交前即可提早寫入至磁盤;這樣的寫入策略靈活性強(qiáng)且性能最好;MySQL InnoDB采用的就是此種寫入方式。

Redo Log

Physiological Logging

在崩潰并重啟后,數(shù)據(jù)庫重放Redo Log進(jìn)行數(shù)據(jù)恢復(fù)時,由于并不知道崩潰前哪些變動的數(shù)據(jù)已經(jīng)寫入到物理磁盤,因此需要保證Redo Log的重放是冪等的,即多次重放得到的結(jié)果不會改變;

InnoDB中所有的數(shù)據(jù)都是以數(shù)據(jù)頁(Page)的形式存在于磁盤中的,因此Redo Log中的每一條日志,會記錄被修改的數(shù)據(jù)頁P(yáng)age ID、被修改的記錄在該P(yáng)age中的位移、記錄中哪些字段被修改了、修改后的字段值:

(Page ID,Record Offset,(Filed 1, Value 1) … (Filed i, Value i) … )

一個事務(wù)可能修改多條記錄(這些記錄可能位于同一個數(shù)據(jù)頁,也可能位于不同的數(shù)據(jù)頁),就會產(chǎn)生多條日志;同時數(shù)據(jù)庫的多個事務(wù)都是并行執(zhí)行的,出于性能的考慮,它們在Redo Log中并非以串行的方式寫入,而是多個事務(wù)產(chǎn)生的多條日志互相穿插在Redo Log中,這就導(dǎo)致了Mini Transaction(Mtr)的產(chǎn)生。Mtr是數(shù)據(jù)庫事務(wù)在Redo Log中的最小存儲單元,一個數(shù)據(jù)庫事務(wù)被劃分為一個或多個Mtr,一個Mtr僅包含對一個數(shù)據(jù)頁的修改(由于一個數(shù)據(jù)頁可能包含多條記錄,因此一個Mtr中包含的日志記錄也不止一條)。

雖然同一個事務(wù)的多個Mtr在Redo Log中可能是不連續(xù)的,但同一個Mtr中包含的多條日志在Redo Log中一定是連續(xù)的。

我們把Redo Log這樣的的存儲方式稱之為Physiological Logging。

LSN機(jī)制

Redo Log不是一個無限膨脹的日志文件,它具有固定的長度,日志先按照物理順序一直往后添加,當(dāng)達(dá)到空間限制后跳轉(zhuǎn)到開始位置重新進(jìn)行寫操作/覆蓋。

Redo Log中的記錄也并非永遠(yuǎn)具有存在的價值,當(dāng)事務(wù)所操作的數(shù)據(jù)已經(jīng)被寫入到物理磁盤中,這個事務(wù)對應(yīng)的日志就沒有存在的意義,事實(shí)上是可以被刪除了。

MySQL數(shù)據(jù)庫使用CheckPoint的值來指定日志文件可以擦除的位置,也就是說該位置之前的日志都是可以刪除的;CheckPoint的值用LSN來表示。

LSN(Log Sequence Number)并非物理位置,它是一個8字節(jié)(64位)的整數(shù)且單調(diào)遞增,代表著自數(shù)據(jù)庫啟動以來,至當(dāng)前時間點(diǎn)寫入至Redo Log中數(shù)據(jù)的總量(字節(jié)數(shù))。

Redo Log中的每一條日志也使用LSN作為它的標(biāo)識。

Double Write Buffer

上文談到數(shù)據(jù)庫通過異步的方式將修改的數(shù)據(jù)寫入至物理磁盤,但如果無法保證數(shù)據(jù)寫入到物理磁盤的原子性,當(dāng)恰好寫入了部分?jǐn)?shù)據(jù)后發(fā)生崩潰,這會導(dǎo)致物理磁盤中存在一個被損壞的數(shù)據(jù)頁;而Redo Log只記錄哪些數(shù)據(jù)頁被修改,但不會記錄哪些數(shù)據(jù)頁被損壞,因此無法通過Redo Log來修復(fù)這些被損壞的數(shù)據(jù)頁;

MySQL InnoDB使用Double Write Buffer來解決寫數(shù)據(jù)至物理磁盤時崩潰后數(shù)據(jù)頁的恢復(fù)問題;

Double Write Buffer是一個存儲區(qū),當(dāng)InnoDB嘗試寫數(shù)據(jù)頁至物理磁盤之前,會先將數(shù)據(jù)頁寫入至該區(qū)域;如果寫數(shù)據(jù)時崩潰,恢復(fù)時InnoDB可以從Double Write Buffer中找到這個被損壞的數(shù)據(jù)頁的完整副本。

正如名稱Double Write所示,這會導(dǎo)致兩次磁盤寫操作:一次是寫入到Double Write Buffer,另外一次是寫入到真正的數(shù)據(jù)頁所在的磁盤位置。

另外一個問題:如果日志從Redo Log Buffer寫入至磁盤時,數(shù)據(jù)庫崩潰了該如何處理?

Redo Log寫入至磁盤是通過日志塊(Log Block)的方式進(jìn)行寫入的,日志塊不同于內(nèi)存中的數(shù)據(jù)頁(1 Page = 16 KB),一個日志塊的大小為512 Byte。每個日志塊中包含該塊的摘要值(CheckSum),通過該摘要值可判斷寫入至磁盤的這個日志塊是否完整。

當(dāng)innodb_flush_log_at_trx_commit設(shè)置為2時,事務(wù)的提交是以日志寫入磁盤作為結(jié)束標(biāo)志的,如果寫入時崩潰,則代表事務(wù)提交失敗,該日志塊實(shí)際上可以直接丟棄。

Undo Log

在上文Redo Log的Physiological Logging中談到,Redo Log只會記錄某個數(shù)據(jù)字段修改后的值,在數(shù)據(jù)庫崩潰后恢復(fù)階段會利用Redo Log對事務(wù)中的數(shù)據(jù)操作進(jìn)行重放,其中包括已提交的事務(wù)和未提交的事務(wù);對于那些未被提交的事務(wù),需要使用Undo Log對其進(jìn)行回滾操作;

與Redo Log使用的Physiological Logging格式不同,Undo Log保存的是邏輯日志;如果事務(wù)執(zhí)行的是一個INSERT操作,Undo Log會保存一條DELETE操作;如果事務(wù)中執(zhí)行的是一個DELETE操作,Undo Log會保存一條INSERT操作;如果事務(wù)執(zhí)行的是一條UPDATE操作,Undo Log中會保存一條反向的UPDATE操作......

Crash Recovery(崩潰恢復(fù))

CheckPoint機(jī)制

InnoDB使用了一個叫做Fuzzy Checkpointing的CheckPoint機(jī)制來實(shí)現(xiàn)數(shù)據(jù)頁寫入至磁盤;它并非一次性的將所有內(nèi)存中的數(shù)據(jù)頁全部寫入到磁盤中,因為這會阻塞在寫入時其他的數(shù)據(jù)庫操作,因此它采用小批量(Small batches)寫入。

當(dāng)數(shù)據(jù)庫崩潰時,并非所有Redo Log中的事務(wù)(包括已提交和未提交的)所修改的數(shù)據(jù)頁都已經(jīng)寫入到了物理磁盤中,我們把那些還未寫入的數(shù)據(jù)頁叫做臟頁(Dirty Pages)。

在崩潰恢復(fù)時,需要找到所有的這些臟頁,并利用Redo Log進(jìn)行重放,也需要找出所有未提交的事務(wù),利用Undo Log進(jìn)行回滾。

由于Fuzzy Checkpointing只是小批量寫入,因此并非所有已提交事務(wù)的數(shù)據(jù)頁都寫入至磁盤中;同時由于多個事務(wù)(包括已提交和未提交的)會修改同一個數(shù)據(jù)頁,這會導(dǎo)致在數(shù)據(jù)頁寫入時可能將未提交事務(wù)的數(shù)據(jù)也寫入到磁盤中了;所以在每一次Fuzzy Checkpointing之后,會把該次Fuzzy Checkpointing時未提交的事務(wù)列表和臟頁列表形成為一個CheckPoint日志,保存到Redo Log中。

在崩潰恢復(fù)過程中,InnoDB引擎會找到Redo Log中最近一次CheckPoint日志,獲取到未提交的事務(wù)列表和臟頁列表,并以該日志為起點(diǎn)遍歷至Redo Log末尾;在遍歷過程中,如果遇到事務(wù)提交,將其從未提交事務(wù)列表中移除,如果遇到新事務(wù)開始,將它加入到未提交事務(wù)列表;同時對遍歷到的所有Physiological Log,都添加到臟頁列表;最后會形成一個最終的未提交事務(wù)列表和臟頁列表。

對臟頁列表,在Redo Log中找到最早的那個臟頁所對應(yīng)的日志,并以此為起點(diǎn)進(jìn)行Redo Log重放。此時可能會遇到的Redo Log對應(yīng)的數(shù)據(jù)頁實(shí)際已經(jīng)寫入至磁盤中了,不過即使再次重放也沒有關(guān)系,因為Redo Log是冪等的。

對所有未提交的事務(wù)列表,找到其對應(yīng)的Undo Log,并進(jìn)行回滾操作。

到此這篇關(guān)于MySQL數(shù)據(jù)庫本地事務(wù)原理的文章就介紹到這了,更多相關(guān)MySQL數(shù)據(jù)庫事務(wù)原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論