mysql事務隔離分類及實現

事務簡介

事務支持是在存儲引擎層實現的。MySQL 是一個支持多引擎的系統,但並不是所有的引擎都支持事務。此處以 InnoDB 爲例,進行剖析。

提到事務,不得不說ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔離性、持久性)。本章主要剖析隔離性有哪幾類,以及如何實現。

SQL 標準的事務隔離級別包括:讀未提交(read uncommitted)、讀提交(read committed)、可重複讀(repeatable read)和串行化(serializable )

  • 讀未提交是指,一個事務還沒提交時,它做的變更就能被別的事務看到。
  • 讀提交是指,一個事務提交之後,它做的變更纔會被其他事務看到。
  • 可重複讀是指,一個事務執行過程中看到的數據,總是跟這個事務在啓動時看到的數據是一致的。當然在可重複讀隔離級別下,未提交變更對其他事務也是不可見的。
  • 串行化,顧名思義是對於同一行記錄,“寫”會加“寫鎖”,“讀”會加“讀鎖”。當出現讀寫鎖衝突的時候,後訪問的事務必須等前一個事務執行完成(釋放鎖),才能繼續執行。

“讀未提交”隔離級別下直接返回記錄上的最新值即可;而“串行化”隔離級別下直接用加鎖的方式來避免並行訪問。而“可重複讀”和“讀提交”隔離級別則需要通過一種“快照”的方式實現,這兩種隔離級別的實現方式幾乎一樣,此處以可重複讀爲例進行剖析。

數據更新及回滾過程

  • InnoDB 裏面每個事務有一個唯一的事務 ID,叫作 transaction id。它是在事務開始的時候向 InnoDB 的事務系統申請的,是按申請順序嚴格遞增的
  • 每行數據是可以有多個版本的。每次事務更新數據的時候,都會生成一個新的數據版本,並在數據版本中記錄 row trx_id = transaction id 。
  • 每條記錄在更新的時候都會同時記錄一條回滾操作。記錄上的最新值,通過回滾操作,都可以得到前一個狀態的值。

注意事項:

  • 這些回滾段,在數據庫中完全不可能用到的時候會被清理掉。完全不可能用到,即不存在與數據版本對應的存活事務。
  • 所以長事務可能導致回滾段數據堆積過多,不能及時清理。即使清理掉 ,文件也不會縮小。

如何實現事務隔離級別爲可重複讀呢?

在清楚了數據如何更新和回滾的基礎上,我們來考慮如何利用 數據版本,事務id,回滾日誌 來實現可重複讀。

大體解決方案如下

  • 以我啓動事務的時刻爲準,如果一個數據版本是在我啓動之前生成的,就認;如果是我啓動以後才生成的,就不認,並通過回滾找“上一個版本”。當然,如果“上一個版本”也不可見,那就得繼續往前找。
  • 如果是這個事務自己更新的數據,它自己是要認的。

具體的實現方案

  • 首先把整個過程分爲啓動前,啓動時,啓動後三個時間段,如下圖

  • 在事務中,數據是否應該被認,如何判斷?如下圖,一行記錄的所有版本數據被分成了三段,這就是我們建立的快照。需要注意的是中間段,如果數據版本對應的事務id,是啓動時活躍的事務id,則不可見。

  • 在事務中,一個查詢的邏輯如下圖所示。目的就是,確保查詢的數據,一定是事務啓動前已經提交的數據,讓整個事務中同樣的查詢每次結果都一致。實現方式就是對事務id進行對比。

  • 在事務中,自己更新的數據,自己是認的。特別需要注意,更新操作,都是基於最新版本數據的。 即先查詢得到最新版本數據,再寫入修改後的數據。

讀提交與可重複讀最主要的區別

  • 在可重複讀隔離級別下,只需要在事務開始的時候打一個快照,之後事務裏的其他查詢都共用這個快照;
  • 在讀提交隔離級別下,每一個語句執行前都會重新打一個快照。

MySQL 的事務啓動方式

  • 顯式啓動事務語句, begin 或 start transaction。配套的提交語句是 commit,回滾語句是 rollback。
  • set autocommit=0,這個命令會將這個線程的自動提交關掉。意味着如果你只執行一個 select 語句,這個事務就啓動了,而且並不會自動提交。這個事務持續存在直到你主動執行 commit 或 rollback 語句,或者斷開連接。有些客戶端連接框架會默認連接成功後先執行一個 set autocommit=0 的命令。這就導致接下來的查詢都在事務中,如果是長連接,就導致了意外的長事務。
  • 因此,我會建議你總是使用 set autocommit=1, 通過顯式語句的方式來啓動事務。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章