天天看點

詳解MySQL事務原理

  • 老劉是即将找工作的研究所學生,自學大資料開發,一路走來,感慨頗深,網上大資料的資料良莠不齊,于是想寫一份詳細的大資料開發指南。這份指南把大資料的【基礎知識】【架構分析】【源碼了解】都用自己的話描述出來,讓夥伴自學從此不求人。
  • 您的點贊是我持續更新的動力,禁止白嫖,看了就要有收獲,一起加油。

今天給大家分享的是大資料開發基礎部分MySQL的事務,事務在MySQL知識點中非常重要的部分,很多夥伴隻是知道MySQL的四大特性,但不知道其中的原理,老劉這次給大家詳細的描述MySQL四大特性的原理,MySQL事務篇的大綱如下:

詳解MySQL事務原理

什麼是事務?

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

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

事務用來管理 DDL、DML、DCL 操作,比如 insert,update,delete 語句,預設是自動送出的。

事務的四大特性(ACID)

  1. Atomicity(原子性):構成事務的的所有操作必須是一個邏輯單元,要麼全部成功,要麼全部失敗。
  2. Consistency(一緻性):資料庫在事務執行前後狀态都必須是穩定的或者是一緻的,就是說事務開始和結束後,資料庫的完整性不會被破壞。
  3. Isolation(隔離性):事務之間不會互相影響。由鎖機制和MVCC機制來實作的,其中MVCC(多版本并發控制):優化讀寫性能(讀不加鎖、讀寫不沖突),四種隔離級别為RU(讀未送出)、RC(讀已送出)、RR(可重複讀)、SERIALIZABLE (串行化)。
  4. 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隔離性中,一般有兩種情況:

  1. 要求同一時刻隻能有一個事務對資料進行寫操作,InnoDB通過鎖機制來保證這一點。
  2. 在進行讀操作的時候,可能出現髒讀、不可重複讀、幻讀的問題。

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

鎖機制的基本原理可以了解為:事務在修改資料之前,需要先獲得相應的鎖;獲得鎖之後,事務便可以修改資料;該事務操作期間,這部分資料是鎖定的,其他事務如果需要修改資料,需要等待目前事務送出或復原後釋放鎖。

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

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

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

  1. 丢失更新:兩個事務針對同一資料都發生修改操作時,會存在丢失更新問題。
  2. 髒讀:對于兩個事務 T1,T2,T1 讀取了已經被 T2 更新但還沒有被送出的字段。之後,若 T2 復原,T1讀取的内容就是臨時且無效的。
  3. 不可重複讀:對于兩個事務T1,T2,T1 讀取了一個字段,然後 T2 更新了該字段。之後,T1再次讀取同一個字段,發現字段的内容不一樣。要求,多次讀取資料的時候,在一個事務中讀出的都應該是一樣的。一般是由于 update 操作引發,是以将來執行的時候要特别注意。
  4. 幻讀:對于兩個事務T1,T2,T1 從一個表中讀取了一個字段,然後 T2 在該表中插入了一些新的行。之後。如果 T1 再次讀取同一個表,就會多出幾行。就是發現資料的數量不一樣。要求,在一個事務中多次去讀取資料的時候都應該是一樣的。

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

  1. Read uncommitted (讀未送出):最低級别,任何情況都無法保證。
  2. Read committed (RC,讀已送出):可避免髒讀的發生。
  3. Repeatable read (RR,可重複讀):可避免髒讀、不可重複讀的發生。(InnoDB預設級别為RR,它可以解決幻讀,主要原因是Next-Key(Gap)鎖,隻有RR才能使用Next-Key鎖)
  4. Serializable (串行化):可避免髒讀、不可重複讀、幻讀的發生。

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

MVCC最大的優點是讀不加鎖,是以讀寫不沖突,并發性能好。InnoDB實作MVCC,多個版本的資料可以共存,主要是依靠資料的隐藏列( 也可以稱之為标記位 )和undo log。其中資料的隐藏列包括了該行資料的版本号、删除時間、指向undo log的指針等等;當讀取資料時,MySQL可以通過隐藏列判斷是否需要復原并找到復原需要的undo log,進而實作MVCC。

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

1、MVCC解決髒讀

詳解MySQL事務原理

當事務T1在第三個時刻讀取自己的餘額時,會發現資料已被T2事務修改,并且T2的狀态還沒有送出。此時事務A讀取最新資料後,根據資料的undo log執行復原操作,得到事務T2修改前的資料,進而避免了髒讀。

2、MVCC解決不可重複讀

詳解MySQL事務原理

當事務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的功能)。

詳解MySQL事務原理

當事務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四大特性的原理。希望大家能夠跟着老劉的文章,好好捋捋思路,争取能夠用自己的話把這些知識點講述出來!

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

大資料開發指南位址如下:
  • github:https://github.com/BigDataLaoLiu/BigDataGuide
  • 碼雲:https://gitee.com/BigDataLiu/BigDataGuide
如果有相關問題,聯系公衆号:努力的老劉。文章都看到這了,點贊關注支援一波!
詳解MySQL事務原理