MySQL事務原理實現與隔離級別

摘要

  本文主要講解MySQL中的事務相關知識點。本文首先介紹什麼是事務,以及事務有哪些屬性,從而使讀者理解MySQL事務。作者繼續引出在併發情景中事務可能出現的問題有哪些,最後講解了解決這些問題的方法,也就是事務的隔離級別。

  本人還寫了MySQL相關博文,有興趣的研友可以點擊如下鏈接,請各位研友指正並留言。
   MySQL索引及優化
   MySQL的鎖及其MVCC

一、事務的定義

   事務是將一系列操作當作最小的、不可分割的工作單元。請讀者注意一個詞“當作”,因爲本人覺得一個事務並不是邏輯上不可分割的,事務在業務層面上纔是最小、不可分割的。
  舉個例子:Damon的銀行賬戶向Tom的銀行賬戶轉100元。
  在這個例子中,按生活中的邏輯是可以分爲兩步完成,第一步Damon銀行賬戶減100;第二步Tom銀行賬戶加100。而從銀行業務層面上看,Damon向Tom轉100是一次性完成的,也就是邏輯上的兩步必須同時完成,或同時失敗,否則業務上是有漏洞的。
  事務也可以看作是關係型數據庫區別於No-SQL數據庫的重要特點之一。

二、事務的屬性

  關於事務的屬性,前人已爲我們總結了如下四點:

  • 原子性(Atomicity):事務是最小單位,不可再分;
  • 一致性(Consistency):事務要求所有的DML語句操作的時候,必須保證同時成功或者同時失敗
  • 隔離性(Isolation):事務A和事務B之間是互不干擾的;
  • 持久性(Durability):事務一旦提交,它對數據庫的改變就應該是永久性的。

  關於事務的原子性和一致性已在第一節的例子中提到,讀者若是不清楚,可以回到第一節的例子中體驗、感受一下原子性和一致性。下面來解釋隔離性和持久性。
  當Damon向Tom轉100的同時Jam也向Tom轉100,那麼將看作兩個事務,事務1:Damon向Tom轉100;事務2:Jam向Tom轉100。兩個事務不能相互干擾,也就是其中一個事務的成功與否都另一個事務的成功與否沒有關係。這便是隔離性。
  一個事務成功了,它的數據信息將保持到硬盤中,不會出現過了三分鐘,系統提示轉賬不成功。這就是持久性。

三、事務屬性的實現原理

  事務的四大屬性是根據MySQL中的日誌來實現的,比如undo log、redo log等。

3.1 原子性的實現原理

  原子性是根據undo log來實現的。原子性強調的是一個事務中某個操作失敗,需要將該事務中所有操作進行回滾。如何進行回滾呢?MySQL在開啓事務時,undo log便開始記錄事務中的所有操作,當需要回滾時,MySQL根據undo log中的記錄,執行其反操作,比如undo log記錄了Tom賬戶加100,那麼回滾時執行其反操作,即Tom賬戶減100。所以undo log可以保證事務的原子性。

3.2 一致性的實現原理

  一致性也是根據undo log來實現的。undo log的介紹如上所述。

3.3 隔離性的實現原理

  隔離性是根據MySQL中的鎖機制來實現的。使用鎖來防止事務與事務之間的相互影響。鎖可以分爲表鎖和行鎖,行鎖又可以分爲讀鎖、寫鎖、間隙鎖等等,鎖的介紹會單獨放到一個博文中,暫不詳解。

3.4 持久性的實現原理

  持久性是根據redo log來是實現的。這與MySQL的數據存儲過程有關。MySQL爲了提高IO的效率,就使用了一個緩衝區buffer pool,MySQL的需要讀取或寫入數據時,都經過buffer pool,再由buffer pool對磁盤數據進行操作,這樣就減少了磁盤的訪問次數,提高了IO效率。但是存在一個問題,但MySQL的服務意外關閉而buffer pool中的數據並沒有完全更新到磁盤數據中,這就不能保證事務的持久性,於是MySQL又用了redo log來解決該問題,MySQL提交事務時,會將相應的數據操作寫入buffer pool的同時也寫入了redo log文件,所以即使MySQL的服務意外關閉,redo log文件也不會消失,服務重新開啓時,MySQL會繼續從redo log中對磁盤數據進行更新,從而保證了數據的持久性。

三、併發情景中的事務問題

  隨着用戶訪問量的劇增,常常伴隨着併發應用情景。那麼事務與事務之間在併發情景中會出現什麼問題呢?可大致歸納爲如下三種情況:

  1. 更新丟失:多個事務基於同一行數據進行操作時,會覆蓋其他事務的更新,導致其他事務的更新丟失。
  2. 髒讀:一個事務讀取了另一個事務還未提交的修改數據。
      比如事務1:Damon向Tom轉100;事務2:Jam向Tom轉100。Tom的賬戶餘額在兩個事務之前爲1000,現在事務2執行到Tom的餘額加100時,Tom的餘額將變爲1100,但事務2還沒有提交,此時事務1中讀取Tom的餘額時,卻變爲了1100,與事務1開始前Tom的餘額不一致。這就是髒讀。
  3. 不可重複讀:一個事務讀取了另一個事務已提交的修改數據。
      同上列子, 事務1、事務2同時開始,事務2執行並提交了數據,當事務1再次讀取Tom的餘額時,已變爲1100,表現出事務1不能重複讀取到Tom的相同餘額值。當另一個事務對該數據操作並提交時,事務1中的數據又發生了變化,這也將引發錯誤。
  4. 幻讀:事務1對數據進行了添加(如,增加一行數據並非修改)但未提交,事務2讀取到了這條添加的數據,隨後事務1進行了回滾。那麼事務2 就產生了幻讀數據。

四、事務隔離級別

  MySQL的存儲引擎InnoDB針對前文提及的事務問題,提供了四種事務隔離級別,分別爲:

  1. 讀未提交(read-uncommitted):一個事務可以讀到另一個事務修改但未提交的數據;
  2. 讀已提交(read-committed):一個事務只能讀到另一個事務修改並提交的數據;
  3. 可重複讀(repeatable-read):一個事務生成快照之後,可重複讀取自己的快照信息,並不關心其他事務對數據修改的提交與否,但該事務執行寫操作時,是以數據庫真實數據進行操作的,而不是快照信息。
  4. 串行化(serializable):將所有事務進行排序,一個一個執行。

  四種事務隔離級別已給出,那麼,這些隔離級別與併發事務問題的對應關係是怎樣的呢?如下表所示:

隔離級別 髒讀 不可重複讀 幻讀
讀未提交 存在 存在 存在
讀已提交 解決 存在 存在
可重複讀 解決 解決 存在
串行化 解決 解決 解決

  在MySQL中如何設置事務呢?如下所示:
  1、開啓事務

begin;

  2、查詢事務隔離級別

select @@tx_isolation;

  3、設置事務隔離級別

set tx_isolation = 'transaction_leve';

transaction_leve可以爲:
a. read-uncommitted
b. read-committed
c. repeatable-read
d. serializable

  4、回滾事務

rollback

  5、提交事務

commit

五、總結

  本文主要介紹了MySQL事務的概念,用通俗易懂例子講解了事務的特性,以及實現這些特性的原理。然後引出了併發情景下事務會產生的問題,最後圍繞這些問題提出解決方法,也就事務的隔離級別。希望本文可以幫助到廣大研友,對於文章中不對、不詳細、不清楚的地方,請研友留言,本人一定會進行改善或作出迴應。

發佈了21 篇原創文章 · 獲贊 0 · 訪問量 1817
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章