mysql事務和隔離級別底層原理淺析

前言

首先回顧一下什麼是事務,事務是數據庫操作的最小工作單元,是作為單個邏輯工作單元執行的一系列操作;這些操作作為一個整體一起向系統提交,要麼都執行、要麼都不執行;事務是一組不可再分割的操作集合(工作邏輯單元)。

事務的特性:

原子性(Atomicity):原子性是指事務包含的所有操作要麼全部成功,要麼全部失敗回滾。一致性(Consistency):事務執行的結果必須是使數據庫從一個一致性狀態變到另一個一致性狀態。隔離性(Isolation):一個事務的執行不能其它事務幹擾。即一個事務內部的操作及使用的數據對其它並發事務是隔離的,並發執行的各個事務之間不能互相幹擾。持久性(Durability):指一個事務一旦提交,它對數據庫中的數據的改變就應該是永久性的。接下來的其它操作或故障不應該對其執行結果有任何影響。

一、事務底層原理淺析

原子性:

實現原理:undo log

undo log也被成為回滾日志,它是事務實現原子性和隔離性的基礎。當事務對數據庫進行修改時,InnoDB會生成對應的undo log;如果事務執行失敗或調用瞭rollback,導致事務需要回滾,便可以利用undo log中的信息將數據回滾到修改之前的樣子。

undo log屬於邏輯日志,它記錄的是sql執行相關的信息。當發生回滾時,InnoDB會根據undo log的內容做與之前相反的工作:對於每個insert,回滾時會執行delete;對於每個delete,回滾時會執行insert;對於每個update,回滾時會執行一個相反的update,把數據改回去。

undo 存放在數據庫內部的一個特殊段segment中,這個段稱為undo段。undo段位於共享表空間中。undo是邏輯日志,因此隻是將數據庫邏輯的恢復到原來的樣子。undo log會產生redo log,也就是undo log的產生會伴隨著redo log的產生,因為undo log也需要持久性的保護。

undo log執行記錄是在每次寫入數據或者修改數據之前。

在這裡插入圖片描述

undo log使用原理:數據庫表每行數據會多兩列DATA_TRX_ID和DATA_ROLL_PTR(可能還有一列DB_ROW_ID,當沒有默認主鍵時會自動加上這列)。DATA_TRX_ID表示當前數據的事務版本, DATA_ROLL_PTR 則指向剛剛拷貝到

undo log 鏈中的舊版本記錄,undo log是個鏈表,如果多個事務多次修改會繼續生成undo log並通過DATA_ROLL_PTR建立指向關系。用圖說明一下:

在這裡插入圖片描述

這樣,一旦事務發生回滾,mysql便可以借助undo log實現數據還原,從而保證未提交事務的原子性。

持久性

實現原理:redo log

由於InnoDB作為MySQL的存儲引擎,數據是存放在磁盤中的,為瞭減少磁盤IO,提高讀取性能InnoDB提供瞭緩存池——Buffer Pool。Buffer Pool中包含瞭磁盤中部分數據頁的映射,作為訪問數據庫的緩沖:當從數據庫讀取數據時,會首先從Buffer Pool中讀取,如果Buffer Pool中沒有,則從磁盤讀取後放入Buffer Pool;當向數據庫寫入數據時,會首先寫入Buffer Pool,Buffer Pool中修改的數據會定期刷新到磁盤中(這一過程稱為刷臟)。

不過這也帶來瞭一個新的問題,如果MySQL宕機,而此時Buffer Pool中修改的數據還沒有刷新到磁盤,就會導致數據的丟失,事務的持久性無法保證。

為瞭解決這個問題就引入瞭redo log,也叫重做日志。當數據修改時,除瞭修改Buffer Pool中的數據,還會在redo log記錄這次操作;當事務提交時,會調用fsync接口對redo log進行刷盤。如果MySQL宕機,重啟時可以讀取redo log中的數據,對數據庫進行恢復。redo log采用的是WAL(Write-ahead logging,預寫式日志),所有修改在提交前先寫入日志,保證瞭數據不會因MySQL宕機而丟失,從而滿足瞭持久性要求。redo log是針對物理頁的,並發執行,最後一次提交會覆蓋未提交的數據。本地redo log:

在這裡插入圖片描述

redo log也是有緩沖區的——redo log buffer,當事務提交之後會把所有修改信息都刷新到磁盤上。用戶也可以通過控制通過變量 innodb_flush_log_at_trx_commit 的值來修改刷新策略(默認1),如設置值為2,控制成每秒刷新,這樣事務提

交就會較快,不過可能面臨日志丟失的風險。

redo log是在SQL語句執行之後記錄的。

在這裡插入圖片描述

既然redo log也需要存儲,也涉及磁盤IO為啥還用它?

(1)redo log 的存儲是順序存儲,而緩存同步是隨機操作。

(2)緩存同步是以數據頁為單位的,每次傳輸的數據大小大於redo log。

redo log是用來恢復數據的 用於保障已提交事務的持久化特性。

隔離性:

原理:
(1). 寫操作對寫操作的影響:鎖機制保證隔離性
(2). 寫操作對讀操作的影響:MVCC保證隔離性

一致性:

一致性比較特殊,前面的原子性、持久性和隔離性都是為一致性服務的,除此之外,一致性還依賴於數據庫自我提供的保障,如SQL語法驗證,列類型插入數據類型驗證,同時也依賴於應用層的保障,如轉賬操作,需要開發人員進行轉賬者的餘額扣除和接受者的餘額增加,如果應用層面出現問題,那麼一致性也是無法保障的。

二、隔離級別底層原理淺析

在事務底層原理淺析中,關於隔離性的原理沒有過多深入,在此我們簡單介紹一下。

首先介紹一下mysql的MVCC(MultiVersion Concurrency Control) 叫做多版本並發控制。它是依賴undo logread view實現的。undo log上文已經介紹過瞭,不再贅述,read view(可讀視圖),這與我們平時理解的數據庫視圖不同,它是用來判斷當前數據版本的可見性的。

readview主要有四個屬性:

(1). m_ids 代表生成ReadView時,當前所有活躍的事務ID,活躍的意思就是事務開啟瞭還沒提交;

(2). min_trx_id 表示當前活躍的mIds中最小的事務ID;

(3). max_trx_id 表示生成ReadView時,最大的事務ID,它不一定是mIds中最大的事務ID;

(4).creator_trx_id表示創建該ReadView的事務ID。

在這裡插入圖片描述

註意:每開啟一個事務,事務ID就自增一次,事務ID可以看做一個全局自增變量。最先開啟的事務不一定比後開啟的事務先提交,比如長連接,所以不要認為max_trx_id就是mIds中的最大值。

read view是怎樣借助上面四個屬性,判斷事務應該讀取那個版本的數據呢?

如果被訪問版本的 data_trx_id 小於 m_ids 中的最小值,說明生成該版本的事務在 ReadView 生成前就已經提交瞭,那麼該版本可以被當前事務訪問。

如果被訪問版本的 data_trx_id 屬性值與ReadView中的creator_trx_id值相同,意味著當前事務在訪問它自己修改過的記錄,所以該版本可以被當前事務訪問。

如果被訪問版本的 data_trx_id 屬性值大於ReadView中的max_trx_id值,表明生成該版本的事務在當前事務生成ReadView後才開啟,所以該版本不可以被當前事務訪問。

如果被訪問版本的 data_trx_id 屬性值在ReadView的min_trx_id和max_trx_id之間,那就需要判斷一下trx_id屬性值是不是在m_ids列表中,如果在,說明創建ReadView時生成該版本的事務還是活躍的,該版本不可以被訪問;如果不在,說明創建ReadView時生成該版本的事務已經被提交,該版本可以被訪問。

當一個事務要讀取一行數據,首先用上面規則判斷數據的最新版本也就是那行記錄,如果發現可以訪問就直接讀取瞭,如果發現不能訪問,就通過DATA_ROLL_PTR指針找到undo log,遞歸往下去找每個版本,知道讀取到自己可以讀取的版本為止,如果讀取不到就返回空。

所以訪問數據時,數據庫裡面會創建一個視圖,訪問的時候以視圖的邏輯結果為準:

READ UNCOMMITED (未提交讀):此隔離級別下直接返回記錄上的最新值,沒有視圖概念。因為讀不會加任何鎖,所以寫操作在讀的過程中修改數據,所以會造成臟讀。好處是可以提升並發處理性能,能做到讀寫並行。

READ COMMITED (提交讀):此隔離級別下,這個視圖是在每個 SQL語句開始執行的時候創建的。InnoDB在 READ COMMITTED,使用排它鎖,讀取數據不加鎖而是使用瞭MVCC機制。或者換句話說他采用瞭讀寫分離機制。

REPEATABLE READ (可重復讀):此隔離級別下,這個視圖是在事務啟動時創建的,整個事務存在期間都用這個視圖。

SERIALIZABLE (串行化):此隔離級別下直接用加鎖的方式來避免並行訪問。

寫到這裡,你也許發現瞭,MySQL中借助MVCC在可重復讀隔離級別下其實也杜絕瞭幻讀的發生。

三、總結

原子性:使用 undo log (回滾日志)實現回滾,從而保障未提交事務的原子性;

持久性:使用 redo log(重做日志)實現數據恢復,從而保障已提交事務的持久性;

隔離性:使用鎖以及MVCC思想實現讀寫分離,讀讀並行,讀寫並行;

一致性:通過回滾、恢復和在並發環境下的隔離做到一致性。

到此這篇關於mysql事務和隔離級別底層原理淺析的文章就介紹到這瞭,更多相關mysql事務和隔離級別內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: