MySQL的四種事務隔離級別


一、事務的基本要素(ACID)

 1、原子性(Atomicity):事務開始后所有操作,要么全部做完,要么全部不做,不可能停滯在中間環節。事務執行過程中出錯,會回滾到事務開始前的狀態,所有的操作就像沒有發生一樣。也就是說事務是一個不可分割的整體,就像化學中學過的原子,是物質構成的基本單位。

 2、一致性(Consistency):事務開始前和結束后,數據庫的完整性約束沒有被破壞 。比如A向B轉賬,不可能A扣了錢,B卻沒收到。

 3、隔離性(Isolation):隔離性是當多個用戶並發訪問數據庫時,比如操作同一張表時,數據庫為每一個用戶開啟的事務,不能被其他事務的操作所干擾,多個並發事務之間要相互隔離。關於事務的隔離性數據庫提供了多種隔離級別,稍后會介紹到。

 4、持久性(Durability):事務完成后,事務對數據庫的所有更新將被保存到數據庫,不能回滾。

二、事務的並發問題

 2.1 臟讀

  就是指當一個事務正在訪問數據,並且對數據進行了修改,而這種修改還沒有提交到數據庫中,這時,另外一個事務也訪問這個數據,然后使用了這個數據。因為這個數據是還沒有提交的數據,那么另外一個事務讀到的這個數據是臟數據,依據臟數據所做的操作可能是不正確的。

  • 1、如果都未更新你就讀取了,或者都更新完才讀取,這都不是臟讀,因為得到的是更新前的有效值,或完全更新后的值。

  • 2、如果那個用戶更新一半你就讀取了,也就是說更新了A,正打算要更新B但尚未更新時,就讀取了,此時得到的就是臟數據。

  避免臟讀的辦法就是采取事務,使得用戶正在更新時鎖定數據庫,阻止你讀取,直至全部完成才讓讀取。

 2.2 不可重復讀

  事務 A 多次讀取同一數據,事務 B 在事務A多次讀取的過程中,對數據作了更新並提交,導致事務A多次讀取同一數據時,結果 不一致。

 2.3 幻讀

  系統管理員A將數據庫中所有學生的成績從具體分數改為ABCDE等級,但是系統管理員B就在這個時候插入了一條具體分數的記錄,當系統管理員A改結束后發現還有一條記錄沒有改過來,就好像發生了幻覺一樣,這就叫幻讀。

  小結:不可重復讀的和幻讀很容易混淆,不可重復讀側重於修改,幻讀側重於新增或刪除。解決不可重復讀的問題只需鎖住滿足條件的行,解決幻讀需要鎖表

三、事務隔離級別

  SQL 標准定義了四種隔離級別,MySQL 全都支持。這四種隔離級別分別是:

  1. 讀未提交(READ UNCOMMITTED)
  2. 讀提交 (READ COMMITTED)
  3. 可重復讀 (REPEATABLE READ)
  4. 串行化 (SERIALIZABLE)

  從上往下,隔離強度逐漸增強,性能逐漸變差。采用哪種隔離級別要根據系統需求權衡決定,其中,可重復讀是 MySQL 的默認級別

       

  事務隔離其實就是為了解決上面提到的臟讀、不可重復讀、幻讀這幾個問題,下面展示了 4 種隔離級別對這三個問題的解決程度。

      

  只有串行化的隔離級別解決了全部這 3 個問題,其他的 3 個隔離級別都有缺陷。

 3.1 讀未提交

   (1)打開一個客戶端A,並設置當前事務模式為read uncommitted(未提交讀),查詢表account的初始值:

    

  (2)在客戶端A的事務提交之前,打開另一個客戶端B,更新表account:  

    

  (3)這時,雖然客戶端B的事務還沒提交,但是客戶端A就可以查詢到B已經更新的數據:

    

  (4)一旦客戶端B的事務因為某種原因回滾,所有的操作都將會被撤銷,那客戶端A查詢到的數據其實就是臟數據:

    

  (5)在客戶端A執行更新語句update account set balance = balance - 50 where id =1,lilei的balance沒有變成350,居然是400,是不是很奇怪,數據不一致啊,如果你這么想就太天真 了,在應用程序中,我們會用400-50=350,如果用該數據去做一些別的操作就會產生一系列的問題,因為程序並不知道其他會話回滾了,要想解決這個問題可以采用讀已提交的隔離級別

    

  總結:我們將事務隔離級別設置為read uncommitted,即便是事務沒有commit,但是我們仍然能讀到未提交的數據,這是所有隔離級別中最低的一種。我們在一個事務中可以隨隨便便讀取到其他事務未提交的數據,這還是比較麻煩的,我們叫臟讀。事務commit后才會更新到數據庫,故數據庫中的數據並沒有改變。

 3.2 讀已提交

  (1)打開一個客戶端A,並設置當前事務模式為read committed(未提交讀),查詢表account的所有記錄:

    

  (2)在客戶端A的事務提交之前,打開另一個客戶端B,更新表account:

    

   (3)這時,客戶端B的事務還沒提交,客戶端A不能查詢到B已經更新的數據,解決了臟讀問題:

    

   (4)客戶端B的事務提交:

    

   (5)客戶端A執行與上一步相同的查詢,結果 與上一步不一致,即產生了不可重復讀的問題

    

  總結:當我們將當前會話的隔離級別設置為read committed的時候,當前會話只能讀取到其他事務提交的數據,未提交的數據讀不到。那就是我們在會話B同一個事務中,讀取到兩次不同的結果。這就造成了不可重復讀,就是兩次讀取的結果不同。這種現象叫不可重復讀。

 3.3 可重復讀

  (1)打開一個客戶端A,並設置當前事務模式為repeatable read,查詢表account的所有記錄

    

   (2)在客戶端A的事務提交之前,打開另一個客戶端B,更新表account並提交

    

   (3)在客戶端A查詢表account的所有記錄,與步驟(1)查詢結果一致,沒有出現不可重復讀的問題

    

   (4)在客戶端A,接着執行update balance = balance - 50 where id = 1,balance沒有變成400-50=350,lilei的balance值用的是步驟(2)中的350來算的,所以是300,數據的一致性倒是沒有被破壞。可重復讀的隔離級別下使用了MVCC機制,select操作不會更新版本號,是快照讀(歷史版本);insert、update和delete會更新版本號,是當前讀(當前版本)。

    

   (5)重新打開客戶端B,插入一條新數據后提交

    

   (6)在客戶端A查詢表account的所有記錄,沒有 查出 新增數據,所以沒有出現幻讀

    

  總結:當我們將當前會話的隔離級別設置為repeatable read的時候,當前會話可以重復讀,就是每次讀取的結果集都相同,而不管其他事務有沒有提交。

     但是不是一定不會出現幻讀呢?由一開始給的表格肯定不是的,在一定情況下還會出現幻讀,接下來會詳細介紹。

 3.4 串行化

  (1)打開一個客戶端A,並設置當前事務模式為serializable,開啟事務,查詢表account的初始值:

  (2)打開一個客戶端B,並設置當前事務模式為serializable,插入一條記錄報錯,表被鎖了插入失敗,mysql中事務隔離級別為serializable時會鎖表,因此不會出現幻讀的情況,這種隔離級別並發性極低,開發中很少會用到。

  總結:當我們將當前會話的隔離級別設置為serializable的時候,其他會話對該表的寫操作將被掛起。可以看到,這是隔離級別中最嚴格的,但是這樣做勢必對性能造成影響。所以在實際的選用上,我們要根據當前具體的情況選用合適的。

  串行化是4種事務隔離級別中隔離效果最好的,解決了臟讀、可重復讀、幻讀的問題,但是效果最差,它將事務的執行變為順序執行,與其他三個隔離級別相比,它就相當於單線程,后一個事務的執行必須等待前一個事務結束。

四、可重復讀怎么解決的幻讀及什么情況下仍然會出現幻讀

 RR隔離級別是會出現幻讀的,mysql的官方文檔 里面解釋說,幻讀問題只有在serializable read level 才能解決。

 4.1 想要出現幻讀,可以按照如下順序測試:

  1. 開啟T1事務,然后查詢數據
  2. 開啟事務T2,插入一條數據,提交事務
  3. 回到事務T1,執行剛才的查詢,發現T2插入的數據不存在,幻讀沒有發生
  4. T1事務里面,修改剛才插入的數據
  5. 再次執行T1的查詢,發現T2的數據出現了,幻讀發生了

  RR隔離級別只能部分解決幻讀,特殊情況還是存在

 4.2 RR 解決幻讀的方法

  為了解決不可重復讀,或者為了實現可重復讀,MySQL 采用了 MVVC (多版本並發控制) 的方式。

  上面介紹可重復讀的時候,那張圖里標示着出現幻讀的地方實際上在 MySQL 中並不會出現,MySQL 已經在可重復讀隔離級別下解決了幻讀的問題。前面剛說了並發寫問題的解決方式就是行鎖,而解決幻讀用的也是鎖,叫做間隙鎖,MySQL 把行鎖和間隙鎖合並在一起,解決了並發寫和幻讀的問題,這個鎖叫做 Next-Key鎖。

  

補充:

  1、事務隔離級別為讀提交時,寫數據只會鎖住相應的行

  2、事務隔離級別為可重復讀時,如果檢索條件有索引(包括主鍵索引)的時候,默認加鎖方式是next-key 鎖;如果檢索條件沒有索引,更新數據時會鎖住整張表。一個間隙被事務加了鎖,其他事務是不能在這個間隙插入記錄的,這樣可以防止幻讀。

  3、事務隔離級別為串行化時,讀寫數據都會鎖住整張表

   4、隔離級別越高,越能保證數據的完整性和一致性,但是對並發性能的影響也越大。

   5、MYSQL MVCC實現機制參考鏈接:https://blog.csdn.net/whoamiyang/article/details/51901888

   6、關於next-key 鎖可以參考鏈接:https://blog.csdn.net/bigtree_3721/article/details/73731377

 

 

參考:

https://www.cnblogs.com/wyaokai/p/10921323.html

https://www.cnblogs.com/CoderAyu/p/11525408.html#idx_2

https://www.jianshu.com/p/4e3edbedb9a8

https://zhuanlan.zhihu.com/p/117476959

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2024 CODEPRJ.COM