面试官:请说一下Mysql事务实现原理
在日常工作中,數(shù)據(jù)庫(kù)是我們必須使用的,其中使用最多的也是大部分中小公司的選擇是Mysql,跳槽面試中也是必問(wèn)的,今天我們就說(shuō)一下Mysql事務(wù)
MySQL中的事務(wù)實(shí)現(xiàn)原理主要涉及以下幾個(gè)方面:
- ACID特性:MySQL支持事務(wù)的原因之一是它遵循ACID(原子性、一致性、隔離性和持久性)特性。這意味著在一個(gè)事務(wù)中的所有操作要么全部成功地提交,要么全部失敗回滾。這確保了數(shù)據(jù)的一致性和可靠性。
- 日志:MySQL使用日志來(lái)記錄事務(wù)的操作和變化。MySQL有兩種主要的日志類型:重做日志(Redo Log)和回滾日志(Undo Log)。
- 鎖機(jī)制:MySQL使用鎖機(jī)制來(lái)實(shí)現(xiàn)事務(wù)的隔離性,保證并發(fā)事務(wù)的正確執(zhí)行。MySQL支持多種類型的鎖,如共享鎖(Shared Lock)和排他鎖(Exclusive Lock),以及行級(jí)鎖和表級(jí)鎖等。鎖機(jī)制可以防止多個(gè)事務(wù)同時(shí)修改同一個(gè)數(shù)據(jù),保證數(shù)據(jù)的一致性。
- MVCC(多版本并發(fā)控制):MVCC是MySQL中的一種并發(fā)控制機(jī)制,用于在并發(fā)事務(wù)執(zhí)行時(shí)保證數(shù)據(jù)的隔離性。MVCC通過(guò)在每個(gè)數(shù)據(jù)行上維護(hù)多個(gè)版本來(lái)實(shí)現(xiàn)。每個(gè)事務(wù)在讀取數(shù)據(jù)時(shí),會(huì)根據(jù)自己的事務(wù)ID和數(shù)據(jù)行的版本信息來(lái)確定可見(jiàn)的數(shù)據(jù)版本,從而實(shí)現(xiàn)不同事務(wù)之間的隔離性。
- 事務(wù)管理器:MySQL有一個(gè)事務(wù)管理器來(lái)協(xié)調(diào)和管理事務(wù)的執(zhí)行。事務(wù)管理器負(fù)責(zé)事務(wù)的開(kāi)始、提交、回滾和并發(fā)控制等。它還負(fù)責(zé)處理并發(fā)事務(wù)之間的沖突和死鎖等問(wèn)題。
綜上所述,MySQL通過(guò)使用日志、鎖機(jī)制、MVCC和事務(wù)管理器等技術(shù)來(lái)實(shí)現(xiàn)事務(wù)的原子性、一致性、隔離性和持久性。這些機(jī)制保證了數(shù)據(jù)的完整性和一致性,并提供了高并發(fā)的支持。
其中ACID四大特性,實(shí)際上分為兩個(gè)部分,其中的原子性、一致性、持久性,實(shí)際上是由InnoDB中的兩份日志來(lái)保證的,一份是redo log日志,一份是undo log日志。而隔離性是通過(guò)數(shù)據(jù)庫(kù)的鎖,加上MVCC來(lái)保證的。
我們?cè)谥v解事務(wù)原理的時(shí)候,主要就是來(lái)研究一下redolog,undolog以及MVCC
事務(wù)基礎(chǔ)ACID
事務(wù)是一組操作的集合,它是一個(gè)不可分割的工作單位,事務(wù)會(huì)把所有的操作作為一個(gè)整體一起向系統(tǒng)提交或撤銷操作請(qǐng)求,即這些操作要么同時(shí)成功,要么同時(shí)失敗。
特性
? 原子性(Atomicity):事務(wù)是不可分割的最小操作單元,要么全部成功,要么全部失敗。
? 一致性(Consistency):事務(wù)完成時(shí),必須使所有的數(shù)據(jù)都保持一致?tīng)顟B(tài)。
? 隔離性(Isolation):數(shù)據(jù)庫(kù)系統(tǒng)提供的隔離機(jī)制,保證事務(wù)在不受外部并發(fā)操作影響的獨(dú)立環(huán)境下運(yùn)行。
? 持久性(Durability):事務(wù)一旦提交或回滾,它對(duì)數(shù)據(jù)庫(kù)中的數(shù)據(jù)的改變就是永久的。
那實(shí)際上,我們研究事務(wù)的原理,就是研究MySQL的InnoDB引擎是如何保證事務(wù)的這四大特性的。
redo log重做日志
記錄的是事務(wù)提交時(shí)數(shù)據(jù)頁(yè)的物理修改,是用來(lái)實(shí)現(xiàn)事務(wù)的持久性。
該日志文件由兩部分組成:重做日志緩沖(redo log buffer)以及重做日志文件(redo log file),前者是在內(nèi)存中,后者在磁盤中。當(dāng)事務(wù)提交之后會(huì)把所有修改信息都存到該日志文件中, 用于在刷新臟頁(yè)到磁盤,發(fā)生錯(cuò)誤時(shí), 進(jìn)行數(shù)據(jù)恢復(fù)使用。
如果沒(méi)有redolog,可能會(huì)存在什么問(wèn)題的?我們一起來(lái)分析一下。我們知道,在InnoDB引擎中的內(nèi)存結(jié)構(gòu)中,主要的內(nèi)存區(qū)域就是緩沖池,在緩沖池中緩存了很多的數(shù)據(jù)頁(yè)。當(dāng)我們?cè)谝粋€(gè)事務(wù)中,執(zhí)行多個(gè)增刪改的操作時(shí),InnoDB引擎會(huì)先操作緩沖池中的數(shù)據(jù),如果緩沖區(qū)沒(méi)有對(duì)應(yīng)的數(shù)據(jù),會(huì)通過(guò)后臺(tái)線程將磁盤中的數(shù)據(jù)加載出來(lái),存放在緩沖區(qū)中,然后將緩沖池中的數(shù)據(jù)修改,修改后的數(shù)據(jù)頁(yè)我們稱為臟頁(yè)。而臟頁(yè)則會(huì)在一定的時(shí)機(jī),通過(guò)后臺(tái)線程刷新到磁盤中,從而保證緩沖區(qū)與磁盤的數(shù)據(jù)一致。而緩沖區(qū)的臟頁(yè)數(shù)據(jù)并不是實(shí)時(shí)刷新的,而是一段時(shí)間之后將緩沖區(qū)的數(shù)據(jù)刷新到磁盤中,假如刷新到磁盤的過(guò)程出錯(cuò)了,而提示給用戶事務(wù)提交成功,而數(shù)據(jù)卻沒(méi)有持久化下來(lái),這就出現(xiàn)問(wèn)題了,沒(méi)有保證事務(wù)的持久性。
那么,如何解決上述的問(wèn)題呢?在InnoDB中提供了一份日志 redo log,接下來(lái)我們?cè)賮?lái)分析一下,通過(guò)redolog如何解決這個(gè)問(wèn)題。
有了redolog之后,當(dāng)對(duì)緩沖區(qū)的數(shù)據(jù)進(jìn)行增刪改之后,會(huì)首先將操作的數(shù)據(jù)頁(yè)的變化,記錄在redo log buffer中。在事務(wù)提交時(shí),會(huì)將redo log buffer中的數(shù)據(jù)刷新到redo log磁盤文件中。過(guò)一段時(shí)間之后,如果刷新緩沖區(qū)的臟頁(yè)到磁盤時(shí),發(fā)生錯(cuò)誤,此時(shí)就可以借助于redo log進(jìn)行數(shù)據(jù)恢復(fù),這樣就保證了事務(wù)的持久性。而如果臟頁(yè)成功刷新到磁盤或或者涉及到的數(shù)據(jù)已經(jīng)落盤,此時(shí)redolog就沒(méi)有作用了,就可以刪除了,所以存在的兩個(gè)redolog文件是循環(huán)寫的。那為什么每一次提交事務(wù),要刷新redo log 到磁盤中呢,而不是直接將buffer pool中的臟頁(yè)刷新到磁盤呢 ?
因?yàn)樵跇I(yè)務(wù)操作中,我們操作數(shù)據(jù)一般都是隨機(jī)讀寫磁盤的,而不是順序讀寫磁盤。而redo log在往磁盤文件中寫入數(shù)據(jù),由于是日志文件,所以都是順序?qū)懙摹m樞驅(qū)懙男剩h(yuǎn)大于隨機(jī)寫。這種先寫日志的方式,稱之為 WAL(Write-Ahead Logging 預(yù)寫日志)。
undo log回滾日志
用于記錄數(shù)據(jù)被修改前的信息 , 作用包含兩個(gè) : 提供回滾(保證事務(wù)的原子性) 和MVCC(多版本并發(fā)控制
undo log和redo log記錄物理日志不一樣,它是邏輯日志。可以認(rèn)為當(dāng)delete一條記錄時(shí),undo log中會(huì)記錄一條對(duì)應(yīng)的insert記錄,反之亦然,當(dāng)update一條記錄時(shí),它記錄一條對(duì)應(yīng)相反的update記錄。當(dāng)執(zhí)行rollback時(shí),就可以從undo log中的邏輯記錄讀取到相應(yīng)的內(nèi)容并進(jìn)行回滾。
Undo log銷毀:undo log在事務(wù)執(zhí)行時(shí)產(chǎn)生,事務(wù)提交時(shí),并不會(huì)立即刪除undo log,因?yàn)檫@些日志可能還用于MVCC。
Undo log存儲(chǔ):undo log采用段的方式進(jìn)行管理和記錄,存放在前面介紹的 rollback segment 回滾段中,內(nèi)部包含1024個(gè)undo log segment。
MVCC
全稱 Multi-Version Concurrency Control,多版本并發(fā)控制。指維護(hù)一個(gè)數(shù)據(jù)的多個(gè)版本,使得讀寫操作沒(méi)有沖突,快照讀為MySQL實(shí)現(xiàn)MVCC提供了一個(gè)非阻塞讀功能。MVCC的具體實(shí)現(xiàn),還需要依賴于數(shù)據(jù)庫(kù)記錄中的三個(gè)隱式字段、undo log日志、readView。
接下來(lái)介紹一下InnoDB引擎的表中涉及到的隱藏字段、undolog 以及 readview。
隱藏字段
當(dāng)我們創(chuàng)建了上面的這張表,我們?cè)诓榭幢斫Y(jié)構(gòu)的時(shí)候,就可以顯式的看到這三個(gè)字段。實(shí)際上除了這三個(gè)字段以外,InnoDB還會(huì)自動(dòng)的給我們添加三個(gè)隱藏字段及其含義分別是:
| 隱藏字段 | 含義 |
|---|---|
| DB_TRX_ID | 最近修改事務(wù)ID,記錄插入這條記錄或最后一次修改該記錄的事務(wù)ID。 |
| DB_ROLL_PTR | 回滾指針,指向這條記錄的上一個(gè)版本,用于配合undo log,指向上一個(gè)版本。 |
| DB_ROW_ID | 隱藏主鍵,如果表結(jié)構(gòu)沒(méi)有指定主鍵,將會(huì)生成該隱藏字段。 |
而上述的前兩個(gè)字段是肯定會(huì)添加的,是否添加最后一個(gè)字段DB_ROW_ID,得看當(dāng)前表有沒(méi)有主鍵,如果有主鍵,則不會(huì)添加該隱藏字段。
undolog
介紹
回滾日志,在insert、update、delete的時(shí)候產(chǎn)生的便于數(shù)據(jù)回滾的日志。
當(dāng)insert的時(shí)候,產(chǎn)生的undo log日志只在回滾時(shí)需要,在事務(wù)提交后,可被立即刪除。
而update、delete的時(shí)候,產(chǎn)生的undo log日志不僅在回滾時(shí)需要,在快照讀時(shí)也需要,不會(huì)立即被刪除。
版本鏈
有一張表原始數(shù)據(jù)為:
DB_TRX_ID : 代表最近修改事務(wù)ID,記錄插入這條記錄或最后一次修改該記錄的事務(wù)ID,是自增的。
DB_ROLL_PTR :由于這條數(shù)據(jù)是才插入的,沒(méi)有被更新過(guò),所以該字段值為null。然后,有四個(gè)并發(fā)事務(wù)同時(shí)在訪問(wèn)這張表。
最終生成記錄數(shù)據(jù):
最終我們發(fā)現(xiàn),不同事務(wù)或相同事務(wù)對(duì)同一條記錄進(jìn)行修改,會(huì)導(dǎo)致該記錄的undolog生成一條記錄版本鏈表,鏈表的頭部是最新的舊記錄,鏈表尾部是最早的舊記錄。
readview
ReadView(讀視圖)是快照讀 SQL執(zhí)行時(shí)MVCC提取數(shù)據(jù)的依據(jù),記錄并維護(hù)系統(tǒng)當(dāng)前活躍的事務(wù)(未提交的)id。
ReadView中包含了四個(gè)核心字段:
| 字段 | 含義 |
|---|---|
| m_ids | 當(dāng)前活躍的事務(wù)ID集合 |
| min_trx_id | 最小活躍事務(wù)ID |
| max_trx_id | 預(yù)分配事務(wù)ID,當(dāng)前最大事務(wù)ID+1(因?yàn)槭聞?wù)ID是自增的) |
| creator_trx_id | ReadView創(chuàng)建者的事務(wù)ID |
而在readview中就規(guī)定了版本鏈數(shù)據(jù)的訪問(wèn)規(guī)則:trx_id 代表當(dāng)前undolog版本鏈對(duì)應(yīng)事務(wù)ID。
| 條件 | 是否可以訪問(wèn) | 說(shuō)明 |
|---|---|---|
| trx_id == creator_trx_id | 可以訪問(wèn)該版本 | 成立,說(shuō)明數(shù)據(jù)是當(dāng)前這個(gè)事務(wù)更改的。 |
| trx_id < min_trx_id | 可以訪問(wèn)該版本 | 成立,說(shuō)明數(shù)據(jù)已經(jīng)提交了。 |
| trx_id > max_trx_id | 不可以訪問(wèn)該版本 | 成立,說(shuō)明該事務(wù)是在 |
ReadView生成后才開(kāi)啟。 |
| min_trx_id <= trx_id <= max_trx_id | 如果trx_id不在m_ids中,是可以訪問(wèn)該版本的 | 成立,說(shuō)明數(shù)據(jù)已經(jīng)提交。 |
不同的隔離級(jí)別,生成ReadView的時(shí)機(jī)不同:
- READ COMMITTED :在事務(wù)中每一次執(zhí)行快照讀時(shí)生成ReadView。
- REPEATABLE READ:僅在事務(wù)中第一次執(zhí)行快照讀時(shí)生成ReadView,后續(xù)復(fù)用該ReadView。
MVCC的實(shí)現(xiàn)原理就是通過(guò) InnoDB表的隱藏字段、UndoLog 版本鏈、ReadView來(lái)實(shí)現(xiàn)的。而MVCC + 鎖,則實(shí)現(xiàn)了事務(wù)的隔離性。而一致性則是由redolog 與 undolog保證。
總結(jié)
以上是生活随笔為你收集整理的面试官:请说一下Mysql事务实现原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 通过腾讯网页快捷登录协议截取 QQ邮箱
- 下一篇: 半小时实现GPT纯血鸿蒙版