天天看點

MySQL事務,MVCC,undo log,redo log——最全總結!

目錄

redo log 與 undo log介紹

 1. redo log,什麼是redo log?

redo log 有什麼作用?

2.undo log,什麼是undo log?

undo log 有什麼作用?

mysql鎖技術以及mvcc基礎

2. mvcc基礎

 事務的實作

總結

前言:最近學習了關于mysql的事務機制,然後做個總結!

事務想要做到什麼效果?

按我了解,無非是要做到可靠性以及并發處理

可靠性:資料庫要保證當insert或update操作時抛異常或者資料庫crash的時候需要保障資料的操作前後的一緻,想要做到這個,我需要知道我修改之前和修改之後的狀态,是以就有了undo log和redo log。

并發處理:也就是說當多個并發請求過來,并且其中有一個請求是對資料修改操作的時候會有影響,為了避免讀到髒資料,是以需要對事務之間的讀寫進行隔離,至于隔離到啥程度得看業務系統的場景了,實作這個就得用mysql 的隔離級别。

下面我首先講實作事務功能的三個技術,分别是日志檔案(redo log 和 undo log),鎖技術以及mvcc,然後再講事務的實作原理,包括原子性是怎麼實作的,隔離型是怎麼實作的等等。最後在做一個總結,希望大家能夠耐心看完。

redo log叫做重做日志,是用來實作事務的持久性。該日志檔案由兩部分組成:重做日志緩沖(redo log buffer)以及重做

MySQL事務,MVCC,undo log,redo log——最全總結!
MySQL事務,MVCC,undo log,redo log——最全總結!

mysql 為了提升性能不會把每次的修改都實時同步到磁盤,而是會先存到boffer pool(緩沖池)裡頭,把這個當作緩存來用。然後使用背景線程去做緩沖池和磁盤之間的同步。

那麼問題來了,如果還沒來的同步的時候當機或斷電了怎麼辦?還沒來得及執行上面圖中紅色的操作。這樣會導緻丢部分已送出事務的修改資訊!

是以引入了redo log來記錄已成功送出事務的修改資訊,并且會把redo log持久化到磁盤,系統重新開機之後在讀取redo log恢複最新資料。

總結:

redo log是用來恢複資料的 用于保障,已送出事務的持久化特性

undo log 叫做復原日志,用于記錄資料被修改前的資訊。他正好跟前面所說的重做日志所記錄的相反,重做日志記錄資料被修改後的資訊。undo log主要記錄的是資料的邏輯變化,為了在發生錯誤時復原之前的操作,需要将之前的操作都記錄下來,然後在發生錯誤時才可以復原。

還用上面那兩張表

MySQL事務,MVCC,undo log,redo log——最全總結!

每次寫入資料或者修改資料之前都會把修改前的資訊記錄到 undo log。

undo log 記錄事務修改之前版本的資料資訊,是以假如由于系統錯誤或者rollback操作而復原的話可以根據undo log的資訊來進行復原到沒被修改前的狀态。

undo log是用來復原資料的用于保障 未送出事務的原子性

1. mysql鎖技術

當有多個請求來讀取表中的資料時可以不采取任何操作,但是多個請求裡有讀請求,又有修改請求時必須有一種措施來進行并發控制。不然很有可能會造成不一緻。

讀寫鎖

解決上述問題很簡單,隻需用兩種鎖的組合來對讀寫請求進行控制即可,這兩種鎖被稱為:

共享鎖(shared lock),又叫做"讀鎖"

讀鎖是可以共享的,或者說多個讀請求可以共享一把鎖讀資料,不會造成阻塞。

排他鎖(exclusive lock),又叫做"寫鎖"

寫鎖會排斥其他所有擷取鎖的請求,一直阻塞,直到寫入完成釋放鎖。

MySQL事務,MVCC,undo log,redo log——最全總結!

通過讀寫鎖,可以做到讀讀可以并行,但是不能做到寫讀,寫寫并行事務的隔離性就是根據讀寫鎖來實作的!!!這個後面再說。

mvcc (multiversion concurrency control) 叫做多版本并發控制。

以上片段摘自《高性能mysql》這本書對mvcc的定義。他的主要實作思想是通過資料多版本來做到讀寫分離。進而實作不加鎖讀進而做到讀寫并行。

mvcc在mysql中的實作依賴的是undo log與read view

undo log :undo log 中記錄某行資料的多個版本的資料。

read view :用來判斷目前版本資料的可見性

MySQL事務,MVCC,undo log,redo log——最全總結!
MySQL事務,MVCC,undo log,redo log——最全總結!

前面講的重做日志,復原日志以及鎖技術就是實作事務的基礎。

事務的原子性是通過 undo log 來實作的

事務的持久性性是通過 redo log 來實作的

事務的隔離性是通過 (讀寫鎖+mvcc)來實作的

而事務的終極大 boss 一緻性是通過原子性,持久性,隔離性來實作的!!!

原子性,持久性,隔離性折騰半天的目的也是為了保障資料的一緻性!總之,acid隻是個概念,事務最終目的是要保障資料的可靠性,一緻性。

1.原子性的實作

什麼是原子性:

一個事務必須被視為不可分割的最小工作機關,一個事務中的所有操作要麼全部成功送出,要麼全部失敗復原,對于一個事務來說不可能隻執行其中的部分操作,這就是事務的原子性。

上面這段話取自《高性能mysql》這本書對原子性的定義,原子性可以概括為就是要實作要麼全部失敗,要麼全部成功。

以上概念相信大家夥兒都了解,那麼資料庫是怎麼實作的呢?就是通過復原操作。

所謂復原操作就是當發生錯誤異常或者顯式的執行rollback語句時需要把資料還原到原先的模樣,是以這時候就需要用到undo log來進行復原,接下來看一下undo log在實作事務原子性時怎麼發揮作用的

1.1 undo log 的生成

假設有兩個表 bank和finance,表中原始資料如圖所示,當進行插入,删除以及更新操作時生成的undo log如下面圖所示:

MySQL事務,MVCC,undo log,redo log——最全總結!

從上圖可以了解到資料的變更都伴随着復原日志的産生:

(1) 産生了被修改前資料(zhangsan,1000) 的復原日志

(2) 産生了被修改前資料(zhangsan,0) 的復原日志

根據上面流程可以得出如下結論:

每條資料變更(insert/update/delete)操作都伴随一條undo log的生成,并且復原日志必須先于資料持久化到磁盤上

所謂的復原就是根據復原日志做逆向操作,比如delete的逆向操作為insert,insert的逆向操作為delete,update的逆向為update等。

思考:為什麼先寫日志後寫資料庫?---稍後做解釋

1.2 根據undo log 進行復原

為了做到同時成功或者失敗,當系統發生錯誤或者執行rollback操作時需要根據undo log 進行復原

MySQL事務,MVCC,undo log,redo log——最全總結!

復原操作就是要還原到原來的狀态,undo log記錄了資料被修改前的資訊以及新增和被删除的資料資訊,根據undo log生成復原語句,比如:

如果在復原日志裡有新增資料記錄,則生成删除該條的語句

如果在復原日志裡有删除資料記錄,則生成生成該條的語句

如果在復原日志裡有修改資料記錄,則生成修改到原先資料的語句

2.持久性的實作

事務一旦送出,其所作做的修改會永久儲存到資料庫中,此時即使系統崩潰修改的資料也不會丢失。

先了解一下mysql的資料存儲機制,mysql的表資料是存放在磁盤上的,是以想要存取的時候都要經曆磁盤io,然而即使是使用ssd磁盤io也是非常消耗性能的。

為此,為了提升性能innodb提供了緩沖池(buffer pool),buffer pool中包含了磁盤資料頁的映射,可以當做緩存來使用:

讀資料:會首先從緩沖池中讀取,如果緩沖池中沒有,則從磁盤讀取在放入緩沖池;

寫資料:會首先寫入緩沖池,緩沖池中的資料會定期同步到磁盤中;

上面這種緩沖池的措施雖然在性能方面帶來了質的飛躍,但是它也帶來了新的問題,當mysql系統當機,斷電的時候可能會丢資料!!!

因為我們的資料已經送出了,但此時是在緩沖池裡頭,還沒來得及在磁盤持久化,是以我們急需一種機制需要存一下已送出事務的資料,為恢複資料使用。

于是 redo log就派上用場了。下面看下redo log是什麼時候産生的

MySQL事務,MVCC,undo log,redo log——最全總結!

既然redo log也需要存儲,也涉及磁盤io為啥還用它?

redo log 的存儲是順序存儲,而緩存同步是随機操作。

緩存同步是以資料頁為機關的,每次傳輸的資料大小大于redo log。

3.隔離性實作

隔離性是事務acid特性裡最複雜的一個。在sql标準裡定義了四種隔離級别,每一種級别都規定一個事務中的修改,哪些是事務之間可見的,哪些是不可見的。

級别越低的隔離級别可以執行越高的并發,但同時實作複雜度以及開銷也越大。

mysql 隔離級别有以下四種(級别由低到高):

read uncommited (未送出讀)

read commited (送出讀)

repeatable read (可重複讀)

serializable (可重複讀)

隻要徹底了解了隔離級别以及他的實作原理就相當于了解了acid裡的隔離型。前面說過原子性,隔離性,持久性的目的都是為了要做到一緻性,但隔離型跟其他兩個有所差別,原子性和持久性是為了要實作資料的可性保障靠,比如要做到當機後的恢複,以及錯誤後的復原。

那麼隔離性是要做到什麼呢?隔離性是要管理多個并發讀寫請求的通路順序。這種順序包括串行或者是并行說明一點,寫請求不僅僅是指insert操作,又包括update操作。

MySQL事務,MVCC,undo log,redo log——最全總結!

總之,從隔離性的實作可以看出這是一場資料的可靠性與性能之間的權衡。

可靠性性高的,并發性能低(比如 serializable)

可靠性低的,并發性能高(比如 read uncommited)

read uncommitted

在read uncommitted隔離級别下,事務中的修改即使還沒送出,對其他事務是可見的。事務可以讀取未送出的資料,造成髒讀。

因為讀不會加任何鎖,是以寫操作在讀的過程中修改資料,是以會造成髒讀。好處是可以提升并發處理性能,能做到讀寫并行。

換句話說,讀的操作不能排斥寫請求。

MySQL事務,MVCC,undo log,redo log——最全總結!

優點:讀寫并行,性能高

缺點:造成髒讀

read committed

一個事務的修改在他送出之前的所有修改,對其他事務都是不可見的。其他事務能讀到已送出的修改變化。在很多場景下這種邏輯是可以接受的。

innodb在 read committed,使用排它鎖,讀取資料不加鎖而是使用了mvcc機制。或者換句話說他采用了讀寫分離機制。

但是該級别會産生不可重讀以及幻讀問題。

什麼是不可重讀?

在一個事務内多次讀取的結果不一樣。

為什麼會産生不可重複讀?

這跟 read committed 級别下的mvcc機制有關系,在該隔離級别下每次 select的時候新生成一個版本号,是以每次select的時候讀的不是一個副本而是不同的副本。

在每次select之間有其他事務更新了我們讀取的資料并送出了,那就出現了不可重複讀

MySQL事務,MVCC,undo log,redo log——最全總結!

repeatable read(mysql預設隔離級别)

在一個事務内的多次讀取的結果是一樣的。這種級别下可以避免,髒讀,不可重複讀等查詢問題。mysql 有兩種機制可以達到這種隔離級别的效果,分别是采用讀寫鎖以及mvcc。

采用讀寫鎖實作:

MySQL事務,MVCC,undo log,redo log——最全總結!

為什麼能可重複度?隻要沒釋放讀鎖,在次讀的時候還是可以讀到第一次讀的資料。

優點:實作起來簡單

缺點:無法做到讀寫并行

采用mvcc實作:

MySQL事務,MVCC,undo log,redo log——最全總結!

為什麼能可重複度?因為多次讀取隻生成一個版本,讀到的自然是相同資料。

優點:讀寫并行

缺點:實作的複雜度高

但是在該隔離級别下仍會存在幻讀的問題,關于幻讀的解決我打算另開一篇來介紹。

serializable

該隔離級别了解起來最簡單,實作也最單。在隔離級别下除了不會造成資料不一緻問題,沒其他優點。

MySQL事務,MVCC,undo log,redo log——最全總結!

4.一緻性的實作

下面舉個例子:zhangsan 從銀行卡轉400到理财賬戶:

假如執行完 update bank set balance = balance - 400;之發生異常了,銀行卡的錢也不能平白無辜的減少,而是復原到最初狀态。

又或者事務送出之後,緩沖池還沒同步到磁盤的時候當機了,這也是不能接受的,應該在重新開機的時候恢複并持久化。

假如有并發事務請求的時候也應該做好事務之間的可見性問題,避免造成髒讀,不可重複讀,幻讀等。在涉及并發的情況下往往在性能和一緻性之間做平衡,做一定的取舍,是以隔離性也是對一緻性的一種破壞。

本文出發點是想講一下mysql的事務的實作原理。

實作事務采取了哪些技術以及思想?

原子性:使用 undo log ,進而達到復原

持久性:使用 redo log,進而達到故障後恢複

隔離性:使用鎖以及mvcc,運用的優化思想有讀寫分離,讀讀并行,讀寫并行

一緻性:通過復原,以及恢複,和在并發環境下的隔離做到一緻性。