深刻理解mysql事務

作爲開發人員對數據庫事務應該都不陌生,可是若是知其然而不知其因此然的話,在開發中不免寫出來的代碼存在bug,本文主要介紹mysql中的事務,重點講解事務的隔離級別。mysql

1. ACID

1.1 原子性

原子性是指事務是一個不可分割的工做單位,事務中的操做要麼所有執行,要麼所有都不執行。
例如:
begin // 開啓事務
A:update user set account=account+1 where id =1;
B:update user set account=account+1 where id =1;
commit
這個事務,執行commit時,在麼兩條語句都執行成功,若是出錯,執行rollback時,兩條語句的操做都會回滾到原始狀態;sql

undo log保證原子性
在操做任何數據以前,首先將數據備份到一個地方(這個存儲數據的地方就是undo log)。而後進行數據的修改,若是用戶出現了錯誤或者用戶執行了rollback語句,系統可用利用undo log中的備份的數據恢復到事務開始以前的狀態。
注意:undo log是邏輯日誌
能夠理解爲:數據庫

  • 當delete一條記錄時,undo log中記錄一條對應的insert記錄
  • 當insert 一條記錄時,undo log中會記錄一條對應的delete記錄
  • 當update一條記錄時,它記錄一條對應相反的udpate記錄

    1.2 一致性

    事務執行前和事務執行後,數據庫的完整性約束不被破壞。即事務的執行是從一個有效狀態轉移到另外一個有效狀態;
    例如:
    tom給jack轉帳50元,若是從tom帳戶減小後系統故障或其它緣由沒有給jack加上50元,而事務尚未執行完畢,數據庫會將整個轉帳過程回滾,保證數據的一致性;併發

    1.3 隔離性

    隔離性是指在併發操做中,不一樣事務之間應該隔離開來,使每一個併發中的事務不會互相干擾;ide

    1.4 持久化

    一旦事務提交成功,事務中全部的數據操做都必須被持久化保存到數據庫中,即便提交事務後,數據庫崩潰,在數據庫重啓時,也必須能保證經過某種機制恢復數據。
    redo log保證持久性
    redo log記錄的是新數據的備份,在事務提交前,只要將redo log持久化便可,不須要將數據持久化。當系統崩潰時,雖然數據沒有持久化,可是redo log已經持久化,系統能夠根據redo log的內容,將全部數據恢復到最新的狀態。.net

    2. 事務引發的問題

    上面介紹了數據庫的事務特性,其中隔離性,是咱們本文中重點須要解說的,設置不一樣的數據庫事務之間的隔離級別,會解決相應的事務併發問題。那麼下面說一下事務都會引發哪些問題:3d

2.1 髒讀

事務A讀取到了事務B已經修改但還沒有提交的數據,若是事務B回滾,A讀取的數據與上次不一致;不符合事務的一致性要求;
例如:A(圖左)和B(圖右)同時開啓事務,A執行以下命令:
深刻理解mysql事務
用戶A並未提交事務,可見在事務B中兩次查詢的結果已經發生變化,讀取了事務A中未提交的數據;日誌

2.2 不可重複讀

事務A讀取到了事務B已經提交的修改數據,不符合隔離性;
深刻理解mysql事務
上圖可見,事務B讀取到了事務A所修改的數據;code

2.3 幻讀

事務A讀取到了事務B提交的新增數據,不符合隔離性;
深刻理解mysql事務
事務B讀取到了事務A提交的新增數據;blog

3. 深刻隔離級別

隔離級別等級:
深刻理解mysql事務

查看隔離級別:

show variables like '%isolation%';
SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation;

設置隔離級別:

全局:SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
會話:SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

3.1 讀未提交

讀未提交是數據庫事務隔離級別最低的級別,它任何問題都沒有解決;
設置數據庫事務隔離級別爲讀未提交

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

深刻理解mysql事務

3.2 讀已提交

讀已提交它主要解決髒讀的問題;
設置數據庫事務隔離級別爲讀已交

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
深刻理解mysql事務
能夠看出,讀已提交已經解決了髒讀的問題;

3.3 可重複讀

設置事務隔離級別:

SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

深刻理解mysql事務
能夠看到,就算事務A已經提交,事務B也不會再讀到事務A提交的數據;
那麼可重複讀是否解決了幻讀呢?
咱們試驗一下:
深刻理解mysql事務
這是怎麼回事呢?不是說好的可重複讀不會解決幻讀的問題嗎?爲何這裏新插入的數據就是沒有在事務B中讀取到呢? (下節解答)

3.4 串行化

串行化就很少說了,它實際上是將事務按排隊的處理方式,可是這樣會使事務很是低率的。

4. mysql的MVCC

咱們先來看個示例:
深刻理解mysql事務
從上面的執行過程能夠看出,在A事務提交以後,B事務雖然執行查詢時沒有 問題,可是在B事務執行更新以後,再查詢時,帳戶直接少了100;這是爲何呢?

1) MVCC原理

InnoDB的MVCC經過每行記錄後面的兩個隱藏字段來實現的,建立時的版本號和刪除時的版本號。每一個事務開始版本號都會遞增。
SELECT:

  • 查找版本早於當前事務版本的數據行,即,行的系統版本號小於或等於事務的系統版本號,這樣能夠確保事務讀取的行,是在事務開始以前已經存在的或者自身事務操做過的。
  • 行的刪除版本,要麼未定義,要麼大於當前事務版本號,這樣能夠確保事務讀取到的行,在事務開始以前未被刪除;
    INSERT:
    爲插入的每一行保存當前系統版本號做爲行的版本號;
    DELETE:
    刪除的每行保存當前系統版本號做爲刪除標記;
    UPDATE:
    插入一行新記錄,保存當前系統版本號爲行版本號,同時,保存當前系統版本號到原來的行的刪除標識;

2) 快照讀和當前讀

快照讀:
讀取的是快照版本,也就是歷史版本;普通的select就是快照讀
當前讀:
讀取的是最新版本,update、delete、insert、select...lock in share mode、select ... for update是當前讀;

3.3節疑惑解答
事務A執行更新以後,提交了事務,而事務B再執行更新的時候,它實際上是一個當前讀,可以讀取到最新的已經被事務A修改後的數據(前提事務A已經提交)。

3) undo log中的版本鏈及ReadView

這塊內容比較複雜,能夠參考:
https://blog.csdn.net/shenchaohao12321/article/details/92801779

相關文章
相關標籤/搜索