談到數據庫事務都要提一下ACID 特性:
原子性(Atomicity):事務中的操作要么全部執行,要么全部不執行。
一致性(Consistency):事務執行前后,數據庫的狀態必須是一致的。
隔離性(Isolation):一個事務的執行不應受其他事務的干擾,每個事務都應有自己的數據視圖。
持久性(Durability):一旦事務提交,其結果就會永久保存。
什么是mvcc
mvcc全稱Multi-Version Concurrency Control ,多版本并發控制,顧名思義是維持了數據庫中數據的多版本;這個機制主要是為了服務事務隔離級別中的READ COMMITTED和REPATEABLE READ兩種隔離級別在多個事務讀取數據的時候能遵守sql標準。
本質:就是通過Read View + Undo Log來實現的,Undo Log中保存了歷史快照,而Read View 用來判斷具體哪一個快照是可見的。
下面具體介紹下,什么是 Undo Log和Read View:
什么是Undo Log
undo log是innodb引擎的一種日志,在事務的修改記錄之前,會把該記錄的原值(before image)先保存起來(undo log)再做修改,以便修改過程中出錯能夠恢復原值或者其他的事務讀取。
數據庫中的每行記錄中,除了保存了我們自己定義的一些字段以外,還有一些重要的隱式字段的:
● db_row_id:隱藏主鍵,如果我們沒有給這個表創建主鍵,那么會以這個字段來創建聚簇索引。
● db_trx_id:對這條記錄做了最新一次修改的事務的ID
● db_roll_ptr:回滾指針,指向這條記錄的上一個版本,其實他指向的就是Undo Log中的上一個版本的快照的地址。
因為每一次記錄變更之前都會先存儲一份快照到undo log中,那么這幾個隱式字段也會跟著記錄一起保存在undo log中,就這樣,每一個快照中都有一個db_trx_id字段表示了對這個記錄做了最新一次修改的事務的ID ,以及一個db_roll_ptr字段指向了上一個快照的地址。這樣就產生了一個版本快照鏈,如下:
什么是Read View
在 RC或者RR事務隔離級別下,當前事務產生select查詢時就會產生read view, RC是每次select都會產生一個read view, RR是只在第一次select的時候產生一個read view 后面的select都是使用這個read view
ReadView 中主要包含4個比較重要的內容:
- m_ids :表示在生成ReadView 時當前系統中活躍的讀寫事務的事務id 列表。
- min_trx_id :表示在生成ReadView 時當前系統中活躍的讀寫事務中最小的事務id ,也就是m_ids 中的最小值。
- max_trx_id :表示生成ReadView 時系統中應該分配給下一個事務的id 值。
小貼士: 注意max_trx_id并不是m_ids中的最大值,事務id是遞增分配的。比方說現在有id為1,2,3這三個事務,之后id為3的事務提交了。那么一個新的讀事務在生成ReadView時,m_ids就包括1和2,min_trx_id的值就是1,max_trx_id的值就是4。
- creator_trx_id :表示生成該ReadView 的事務的事務id 。小貼士: 我們前邊說過,只有在對表中的記錄做改動時(執行INSERT、DELETE、UPDATE這些語句時)才會為事務分配事務id,否則在一個只讀事務中的事務id值都默認為0。
而判斷數據記錄可見性的邏輯就是通過readview和【行記錄的隱藏字段trx_id】做對比的
一個事務去訪問記錄的時候,怎么判斷記錄的可見性呢?
Read View決定當前事務能讀到哪個版本的數據,從表記錄到Undo Log歷史數據的版本鏈,依次匹配,滿足哪個版本的匹配規則,就能讀到哪個版本的數據,一旦匹配成功就不再往下匹配。
遵循了以下可見性匹配規則:
規則說明:
- trx_id = creator_trx_id:如果 trx_id 值等于創建Read View的事務Id,那么數據記錄的最后一次操作的事務就是當前事務,該版本的記錄對當前事務可見
- trx_id < min_trx_id:如果 trx_id 值小于 Read View 中的 min_trx_id ,表示這個版本的記錄是在創建 Read View 前已經提交的事務生成的,所以該版本的記錄對當前事務可見
- trx_id >= max_trx_id:如果trx_id 值小于 Read View 中的 min_trx_id ,表示這個版本的記錄是在創建 Read View 后才啟動的事務生成的,所以該版本的記錄對當前事務不可見
- min_trx_id <= trx_id < max_trx*id:判斷 *trx_id 是不是在當前事務ID集合(m_ids)里面
- 如果在m_ids中,則代表Read View生成時刻,這個事務還在活躍,還沒有Commit,版本記錄在前事務不可見
- 如果不在m_ids中,則說明,這個事務在Read View生成之前就已經Commit了,版本記錄在前事務可見
mvcc有什么作用
1.解決臟讀問題:一個事務中的每一次SELECT都會重新獲取一次Read View。
2.解決不可重復讀問題:一個事務中只在第一次SELECT的時候會獲取一次Read View。
3.解決部分幻讀問題:mvcc+間隙鎖可以解決部分幻讀問題,但是不能完全解決。
參考資料
https://dev.mysql.com/doc/refman/5.7/en/innodb-consistent-read.html
https://www.cnblogs.com/ykmStudy/p/17765346.html