java面試一日一題:mysql事務是如何實現的

  • 2021 年 4 月 11 日
  • 筆記

問題:請講下mysql的事務是如何實現的

分析:該問題主要考察對事務的理解及實現方式;

回答要點:

主要從以下幾點去考慮,

1、對事務的概念的理解?

2、事務的實現方式?

 

講到mysql的事務,很快可以想到事務的4大特性,那就是ACID,具體說來就是原子性、一致性、隔離性、持久性。也就是說事務就是圍繞這4個特性來展開的。其中隔離性中又定義了隔離級別,有讀未提交、讀已提交、可重複度、可串列化這樣4個級別。

對於事務的4個特性,原子性、一致性、持久性說的都是mysql的可靠性方面的考量,對應隔離性說的則是在並發場景下,同時有讀和寫的情況如何做到數據隔離,到底數據要隔離到什麼程度,則就有了隔離級別的概念。

 

mysql要保證事務的4大特性,主要使用了日誌(redo log、undo log)、鎖技術、MVCC技術

日誌

說到日誌,平時接觸最多的是mysql的binlog日誌,主要用來記錄mysql執行的更新語句,用在主從複製的場景比較多。今天來說的日誌卻不包含binlog,今天來說redo log和undo log

redo log

又叫重做日誌,用來實現事務的持久性,包含重做緩衝區和重做日誌文件兩部分,前者在記憶體中,後者在磁碟。mysql在事務提交前會把所有的修改資訊都記錄在redo log中。

為什麼要引入redo log那,原來mysql為了提高性能不會把每次的修改都實時同步到磁碟中,而是先存到buffer pool中,然後後台有一個執行緒來進行buffer pool和磁碟之間的同步。這樣就存在一個問題,如果還沒來得及進行buffer pool和磁碟的同步,機器宕機了怎麼辦,那麼數據豈不是要丟失,為了解決這個問題,引入了redo log,在事務開始時把修改記錄到重做緩衝區,在事務提交前將重做緩衝區的內容刷到磁碟上,不禁要問這不是多此一舉嗎,我實時把buffer pool中的內容寫入磁碟不也可以達到這樣的目的,哈哈,肯定不是這樣的,因為redo log的文件是順序寫,而buffer pool和磁碟的寫是隨機寫,順序寫的速度要比隨機寫快很多,所以這就是引入redo log的目的。

undo log

又叫回滾日誌,用來保證事務的原子性。用來記錄數據被修改前的資訊,和redo log正好相反,redo log是記錄修改以後的記錄。undo log記錄的是數據的邏輯變化,為了在發生錯誤時進行數據的回滾操作。

鎖技術

當多個請求同時到達mysql伺服器,如果都是讀請求,那麼可以不採取任何的措施,如果既有讀請求又有寫請求的前提下,必須要有一種機制來規範讀寫請求,不然很容易造成數據的不一致,這時就有了鎖機制,使用讀鎖和寫鎖進行控制即可,讀鎖對所有的讀操作都是共享的,不會造成阻塞;寫鎖則是排他鎖,不能做到寫讀、寫寫並行。

 

MVCC

MVCC叫做多版本並發控制,通過在每條記錄的後邊保存兩個隱藏的列來實現,一個保存行創建的時間,一個保存行的過期時間,保存的不是時間而是系統版本號,通過MVCC來做到讀寫分離。

 

在事務的4大特性中隔離性是最複雜的,4種隔離級別分別定義了一個事務種的修改哪些是事務之間可見的,哪些是事務之間不可見的。

讀未提交

讀未提交是讀的情況下不加任何的鎖,所以會讀到其他事務未提交的數據,造成臟讀數據

讀已提交

在該隔離級別下,讀操作是不加鎖的,採用MVCC的方式進行讀取;寫操作加排他鎖,也就是寫鎖。該級別會產生不可重複讀和幻讀的情況。

不可重複讀指的是兩次的讀數據內容發生了變化,行數未變,針對update操作;幻讀是兩次的讀數據內容未變,行數發生了變化,針對insert、delete操作;

在讀已提交隔離級別下實現的MVCC機制是這樣的,每次的select操作都會生成一個新的版本號,所以每次的讀取都是不同的副本,那麼就存在不可重複讀和幻讀

可重複讀(mysql默認隔離級別)

在一個事務中多次的讀取結果是一樣的。有兩種機制可以實現這種效果,分別是讀寫鎖和MVCC。

使用讀寫鎖實現的話,優點是實現簡單,但是讀寫無法並行。

使用MVCC機制實現優點是實現複雜,但讀寫可並行。在這種情況下實現的MVCC,每次的select操作都是使用最開始的版本號,所以每次的讀取都是相同的數據,不會產生不可重複讀,但是會產生幻讀的情況,mysql通過next-key鎖(行鎖+間隙鎖)的方式解決了幻讀。

 

參考://www.sohu.com/a/316482862_663371

//www.cnblogs.com/zhiqian-ali/p/5668199.html