ACID之I:事務隔離

瑜戈發表於2018-11-25

MyISAM 不支援事務, innoDB支援事務

多個事務可能存在問題

在多個事務共同操作時容易出現這樣幾個問題:

  • 髒讀(dirty read)
  • 不可重複讀(nonrepeatable)
  • 幻讀(phantom read)

來具體看一下這三個問題會有什麼影響

髒讀:A事務讀取到並使用了B事務還未提交的資料,這時如果B事務被撤回,則A操作的資料則是不準確的。比如以下銀行存取款例子:

A事務 B事務
開啟事務
開啟事務
查詢餘額為1000
取出500,餘額變為500
查詢餘額為500(髒資料)
撤銷事務,此時餘額為1000
存入100,此時餘額為600
提交事務

不可重複讀: A事務讀取了B事務已經提交的資料。同一個事務內查詢到不同的結果,比如A查詢到銀行賬戶有200元,然後準備取錢,這時B用同一個賬號將錢轉走,A取錢時會提示餘額不足,A在同一個事務內發現餘額發生了變化,第一次查到的結果不可重複
幻讀:解決了不可重複讀之後,保證同一個事務裡,查詢的結果都是事務開始時的狀態(一致性)。但是,如果另一個事務同時插入了新的資料,則本事務再次更新時,就會發現這些新插入的資料,好像之前讀到的不完整的資料是鬼影一樣幻覺

隔離級別

為了解決髒讀、不可重複讀、幻讀,有了"隔離級別這個概念"
SQL隔離級別包括:

  • 讀未提交(read uncommitted):別人修改資料的事務還沒有提交,我在我的事務內也能讀到
  • 讀可提交(read committed):別人修改資料的事務提交後,我在我的事務內才能讀到
  • 可重複讀(repeatable read):別人修改資料的事務提交後我也讀不到,我只能讀到我事務開始時保持的資料狀態,我的事務內資料不受到其他事務的影響
  • 序列化(serializable):我的事務還沒提交時,比我晚開始的事務只能是等待狀態,同時只能有一個事務在進行

比如我的賬戶有500元,按照時間順序執行以下兩個事務

A事務 B事務
啟動事務A查詢餘額為500
啟動事務B查詢到餘額500
存入100元
再次查詢餘額Q1
提交事務B
查詢餘額Q2
提交事務A
再次查詢餘額Q3

根據不同的隔離級別,Q1、Q2、Q3會有不同的值。下面來詳細分析:

  • 讀未提交(read uncommitted):能夠讀到其他事務未提交的資料,也就是B事務存入100雖然沒有提交,但是事務A第二次查詢餘額已經發生變化,Q1為600,後面沒有涉及到金額的變化,所以Q2/Q3的值也是600
  • 讀可提交(read committed):能夠讀到其他事務已經提交的資料,查詢Q1時,因為B事務還沒有提交,所以並沒有影響到A事務,所以Q1的值還是500,當查詢Q2時,B事務已經提交,A事務可以讀到,所以Q2、Q3都是600
  • 可重複讀(repeatable read):一個事務內的資料不受其他事務的影響,因此在A事務提交前,所以查到的餘額不變,同事務開始時的值是一樣的,因此Q1、Q2都為500。A事務提交後,可以發現B事務造成的資料影響,所以Q3的值為600
  • 序列化(serializable):事務A開始後,事務B再發起,則會被鎖住,當事務A提交後,事務B才能進入,所以事務A提交前Q1、Q2的值是不變的都是500,提交事務A後,事務B才開始解鎖執行,B提交後,Q3查詢到的值為600

由以上可以看出這4種隔離級別,從上至下效能依次降低,安全性依次提高。

事務的啟動方式

事務有兩種啟動方式:

  1. 顯式啟動事務語句,begin或者start transaction,提交commit,回滾rollback
  2. set autocommit=0,該命令會把這個執行緒的自動提交關掉。這樣只要執行一個select語句,事務就啟動,並不會自動提交,直到主動執行commit或rollback或斷開連線。

本文為極客時間《MySQL實戰45講》的學習筆記,其中含有部分原文,如有侵權行為請聯絡我立刻刪除
第二節:MySQL系列之一條更新SQL的生命歷程

相關文章