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

迷茫中守候發表於2021-04-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鎖(行鎖+間隙鎖)的方式解決了幻讀。

 

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

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

相關文章