More  

小編的世界 優質文選 資料

詳解MySQL事務原理


2021年2月07日 - 資料小編  
   

老劉情感影視

什麼是事務?

在MySQL中的事務是由存儲引擎實現的,而且支持事務的存儲引擎不多,我們主要講解InnoDB存儲引擎中的事務。

事務處理可以用來維護數據庫的完整性,保證成批的 SQL 語句要麼全部執行,要麼全部不執行。

事務用來管理 DDL、DML、DCL 操作,比如 insert,update,delete 語句,默認是自動提交的。事務的四大特性(ACID)

Atomicity(原子性):構成事務的的所有操作必須是一個邏輯單元,要麼全部成功,要麼全部失敗。

Consistency(一致性):數據庫在事務執行前後狀態都必須是穩定的或者是一致的,就是說事務開始和結束後,數據庫的完整性不會被破壞。

Isolation(隔離性):事務之間不會相互影響。由鎖機制和MVCC機制來實現的,其中MVCC(多版本並發控制):優化讀寫性能(讀不加鎖、讀寫不沖突),四種隔離級別為RU(讀未提交)、RC(讀已提交)、RR(可重複讀)、SERIALIZABLE (串行化)。

Durability(持久性):事務執行成功後必須全部寫入磁盤,事務提交後,對數據的修改是永久性的,即使系統故障也不會丟失。

事務的使用

begin或start transaction:開啟一個事務;

commit:提交一個事務,並使已對數據庫進行的所有修改稱為永久性的;

rollback:回滾會結束用戶的事務,並撤銷正在進行的所有未提交的修改。ACID實現原理

下面我們就來詳細講解一下上述示例涉及的事務的ACID特性的具體實現原理。總結來說,事務的隔離性由多版本控制機制和鎖實現,而原子性、一致性和持久性通過InnoDB的redo log、undo log和ForceLog at Commit機制來實現。

重做日志Redo Log

如果要存儲數據則先存儲數據的日志,一旦內存崩了,則可以從日志找重做日志保證了數據的可靠性,InnoDB采用了Write Ahead Log(預寫日志)策略,即當事務提交時,先寫重做日志,然後再擇時將髒頁寫入磁盤。如果發生宕機導致數據丟失,就通過重做日志進行數據恢複。

回滾日志Undo Log

數據庫崩潰重啟後需要從redo log中把未落盤的髒頁數據恢複出來,重新寫入磁盤,保證用戶的數據不丟失。當然,在崩潰恢複中還需要回滾沒有提交的事務。由於回滾操作需要undo日志的支持,undo日志的完整性和可靠性需要redo日志來保證,所以崩潰恢複先做redo恢複數據,然後做undo回滾。

所以,在事務執行的過程中,除了記錄redo log,還會記錄一定量的undo log。undo log記錄了數據在每個操作前的狀態,如果事務執行過程中需要回滾,就可以根據undo log進行回滾操作。

Force Log at Commit機制

它實現事務的持久性,即當事務提交時,必須先將該事務的所有日志寫入到重做日志文件進行持久化,然後事務的提交操作完成才算完成。為了確保每次日志都寫入到重做日志文件,在每次將重做日志緩沖寫入重做日志後,必須調用一次fsync操作(操作系統),將緩沖文件從文件系統緩存中真正寫入磁盤。

總結一下就是redo log用於在崩潰時恢複數據,undo log用於對事務的影響進行撤銷,也可以用於多版本控制。而Force Log at Commit機制保證事務提交後redo log日志都已經持久化。

原子性

原子性是指一個事務是一個不可分割的工作單位,其中的操作要麼都做,要麼都不做。例如銀行轉賬要麼成功,要麼失敗,是不存在中間的狀態!

Undo Log是實現原子性的關鍵,靠的就是undo log。當事務對數據庫進行修改時,InnoDB會生成對應的undo log。undo log它屬於邏輯日志,它記錄的是sql執行相關的信息。當發生回滾時,InnoDB會根據undo log的內容做與之前相反的工作:對於每個insert,回滾時會執行delete;對於每個delete,回滾時會執行insert;對於每個update,回滾時會執行一個相反的update,把數據改回去。

以update操作為例:當事務執行 update 時,其生成的 undo log 中會包含被修改行的主鍵(以便知道修改了哪些行)、修改了哪些列、這些列在修改前後的值等信息,回滾時便可以使用這些信息將數據還原到 update 之前的狀態。

持久性

持久性是指事務執行成功後必須全部寫入磁盤,事務提交後,對數據的修改是永久性的,即使系統故障也不會丟失。

InnoDB作為MySQL的存儲引擎,數據是存放在磁盤中的,但如果每次讀寫數據都需要磁盤IO,效率會很低。為此,InnoDB提供了緩存(Buffer Pool),Buffer Pool中包含了磁盤中部分數據頁的映射,作為訪問數據庫的緩沖:當從數據庫讀取數據時,會首先從Buffer Pool中讀取,如果Buffer Pool中沒有,則從磁盤讀取後放入Buffer Pool;當向數據庫寫入數據時,會首先寫入Buffer Pool,Buffer Pool中修改的數據會定期刷新到磁盤中。

雖然Buffer Pool的使用大大提高了讀寫數據的效率,但是也有別的問題,當MySQL宕機,而此時Buffer Pool中修改的數據還沒有刷新到磁盤,就會導致數據的丟失,事務的持久性無法保證。

於是,優秀的程序員們引入了redo log,當我們對數據進行修改時,除了修改Buffer Pool中的數據,還會在redo log中記錄這次操作。當事務提交時,會調用fsync接口對redo log進行刷盤。如果MySQL宕機,重啟時可以讀取redo log中的數據,對數據庫進行恢複。

還有一點必須知道就是redo log采用的是WAL策略,所有修改先寫入日志,再更新到Buffer Pool,保證了數據不會因MySQL宕機而丟失,從而滿足了持久性要求。

隔離性

在MySQL隔離性中,一般有兩種情況:

要求同一時刻只能有一個事務對數據進行寫操作,InnoDB通過鎖機制來保證這一點。

在進行讀操作的時候,可能出現髒讀、不可重複讀、幻讀的問題。

首先講第一種情況,MySQL要求同一時刻只能有一個事務對數據進行寫操作,InnoDB通過鎖機制來保證這一點。

鎖機制的基本原理可以理解為:事務在修改數據之前,需要先獲得相應的鎖;獲得鎖之後,事務便可以修改數據;該事務操作期間,這部分數據是鎖定的,其他事務如果需要修改數據,需要等待當前事務提交或回滾後釋放鎖。

至於鎖機制中的鎖,一般就是之前講到的MySQL鎖,大家可以去看看這篇MySQL鎖的內容。

接著講第二種情況,讀操作可能出現髒讀、不可重複讀、幻讀的問題。

隔離性追求的是並發情形下事務之間互不干擾,但是在事務的並發操作中可能會出現一些問題:

丟失更新:兩個事務針對同一數據都發生修改操作時,會存在丟失更新問題。

髒讀:對於兩個事務 T1,T2,T1 讀取了已經被 T2 更新但還沒有被提交的字段。之後,若 T2 回滾,T1讀取的內容就是臨時且無效的。

不可重複讀:對於兩個事務T1,T2,T1 讀取了一個字段,然後 T2 更新了該字段。之後,T1再次讀取同一個字段,發現字段的內容不一樣。要求,多次讀取數據的時候,在一個事務中讀出的都應該是一樣的。一般是由於 update 操作引發,所以將來執行的時候要特別注意。

幻讀:對於兩個事務T1,T2,T1 從一個表中讀取了一個字段,然後 T2 在該表中插入了一些新的行。之後。如果 T1 再次讀取同一個表,就會多出幾行。就是發現數據的數量不一樣。要求,在一個事務中多次去讀取數據的時候都應該是一樣的。

雖然有上述這些問題,但MySQL數據庫為我們提供的四種隔離級別(由低到高):

Read uncommitted (讀未提交):最低級別,任何情況都無法保證。

Read committed (RC,讀已提交):可避免髒讀的發生。

Repeatable read (RR,可重複讀):可避免髒讀、不可重複讀的發生。(InnoDB默認級別為RR,它可以解決幻讀,主要原因是Next-Key(Gap)鎖,只有RR才能使用Next-Key鎖)

Serializable (串行化):可避免髒讀、不可重複讀、幻讀的發生。

解決髒讀、不可重複讀、幻讀的問題使用的是MVCC,即多版本的並發控制協議。它說的就是在同一時刻,不同的事務讀取到的數據可能是不同的(即多版本)。

MVCC最大的優點是讀不加鎖,因此讀寫不沖突,並發性能好。InnoDB實現MVCC,多個版本的數據可以共存,主要是依靠數據的隱藏列( 也可以稱之為標記位 )和undo log。其中數據的隱藏列包括了該行數據的版本號、刪除時間、指向undo log的指針等等;當讀取數據時,MySQL可以通過隱藏列判斷是否需要回滾並找到回滾需要的undo log,從而實現MVCC。

MVCC如何解決髒讀、不可重複讀、幻讀的問題

1、MVCC解決髒讀

當事務T1在第三個時刻讀取自己的餘額時,會發現數據已被T2事務修改,並且T2的狀態還沒有提交。此時事務A讀取最新數據後,根據數據的undo log執行回滾操作,得到事務T2修改前的數據,從而避免了髒讀。

2、MVCC解決不可重複讀

當事務T1在第二個時刻第一次讀取數據時,會記錄該數據的版本號(數據的版本號是以row為單位記錄的),假設版本號為1;當事務T2對自己的餘額進行修改並且提交時,該行記錄的版本號增加,假設版本號為2;當事務T1在第五個時刻再一次讀取數據時,發現數據的版本號2大於第一次讀取時記錄的版本號1,因此會根據undo log執行回滾操作,得到版本號為1時的數據,從而實現了可重複讀。

3、MVCC解決幻讀

InnoDB實現的RR通過next-key lock機制避免了幻讀現象。

next-key lock是行鎖的一種,實現相當於record lock(記錄鎖) + gap lock(間隙鎖),它的特點是不僅會鎖住記錄本身(record lock的功能),還會鎖定一個範圍(gap lock的功能)。

當事務T1在第二個時刻第一次讀取0<id<5數據時,會進行標記,標記內容包括數據的版本號等,並且標記的不只是id=1的數據,還將範圍(0,5)進行了標記。我們接著在第三個時刻插入新的用戶並且提交事務,最後第五個時刻再次讀取0<id<5數據時,便可以發現id=2的數據比之前標記的版本號更高,此時再結合undo log執行回滾操作,避免了幻讀。

稍微總結下,InnoDB通過鎖機制、數據的隱藏列、undo log和類next-key lock,實現了一定程度的隔離性,可以滿足大多數場景的需要。不過需要說明的是,RR雖然避免了幻讀問題,但是畢竟不是Serializable,不能保證完全的隔離。

一致性

一致性是事物追求的最終目標
,前面提到的原子性,隔離性,持久性都是為了保證數據庫的一致性。也就是說ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔離性)、D(持久性)是手段,是為了保證一致性,數據庫提供的手段。數據庫必須要實現AID三大特性,才有可能實現一致性。總結

本文作為大數據開發指南MySQL的第四篇詳細介紹了MySQL事務的內容,尤其是MySQL四大特性的原理。希望大家能夠跟著老劉的文章,好好捋捋思路,爭取能夠用自己的話把這些知識點講述出來!

盡管當前水平可能不及各位大佬,但老劉會努力變得更加優秀,讓各位小夥伴自學從此不求人!

文章都看到這了,點贊關注支持一波!。

  大家在看