多版本并發控制:Multi-Version Concurrency Control。
-
MySQL里為什么要用MVCC呢?
- 解決讀寫帶來的問題:讀已提交和不可重復讀
- 無需加鎖,提高讀寫效率
-
什么是讀已提交?
- 事務中只能讀到其他事務已提交的更新
- 舉例:
- user表中當前id=1的數據中name=“張三”
- 開啟事務A,將“張三”改為“李四”;
- 事務B開啟,
select name from user where id=1
若事務A還未提交,此時讀到的是“張三”;
-
若事務A已提交,此時讀到的是“李四”;
若事務A未提交時,事務B讀到的就是“李四”,這種情況稱為發生了臟讀;也就是讀未提交,Read Uncommitted。
-
什么是可重復讀
- 事務中對同一行數據的多次讀取結果相同,不受其他事務的影響
- 舉例
- user表中當前id=1的數據中name=“張三”
- 開啟事務A,將“張三”改為“李四”;
- 事務B開啟,
select name from user where id=1
- 若事務A還未提交,此時讀到的是“張三”;
- 若事務A已提交,此時讀到的還是“張三”;
-
如何解決?
- 使用undo log和readview
-
什么是undo log?
事務有ACID四大特性,其中A為原子性,也就是事務中的操作要么全部完成,要么所有操作都不受影響。
為了保證事務的原子性,MySQL中對所有操作保留了歷史記錄,在事務回滾時使用這種日志將之前的操作撤回到未修改前。這種日志即為
undo log
。-
mysql的每行數據中除了用戶定義的字段外,還有兩個隱藏字段:
trx_id
和roll_pointer
,undo log
中也有這兩個隱藏字段。-
trx_id
表示該行undo log由哪個事務產生的 -
roll_pointer
可理解為一種指針,作用是為了將同一行數據的undo log串起來,形成undo log鏈。
mvcc -
-
什么是ReadView?
- 在RC和RR隔離級別下,為了保證數據的隔離性,也就是哪個數據在哪個事務版本中可見,因此在事務執行過程中引入了ReadView的設計。
- ReadView中記錄了幾個重要的數據:
-
m_ids
:生成ReadView時活躍的事務id列表 -
min_trx_id
:m_ids
里最小的事務id -
max_trx_id
:生成ReadView時系統應該分配的下一個事務id -
creator_trx_id
:創建改ReadView的事務id
-
-
如何利用ReadView解決可見性?
- 如果被訪問版本的
trx_id
=creator_trx_id
,證明該版本的數據是由當前事務更新的,可見。 - 如果被訪問版本的
trx_id
小于min_trx_id
,表明生成該版本的數據在生成ReadView時已提交,可見。 - 如果被訪問版本的
trx_id
大于等于max_trx_id
,表明生成該版本的數據在生成ReadView時還未開啟,不可見。 - 如果被訪問版本的
trx_id
在min_trx_id
和max_trx_id
之間,要看trx_id
是否在m_ids
中- 若在,表明生成ReadView時該版本的事務還是活躍的,不可見;
- 若不在,表明生成ReadView時該版本的事務已提交,可見。
- 如果被訪問版本的
-
如何解決隔離性?
- RC
- 每次讀數據時都生成一個ReadView
- 這樣每次都能讀到已提交事務的更新
- 舉例:
-
當前trx_id
為5 - 第一次生成的ReadView中
m_ids
為[3,5,6] - 第二次生成的ReadView為[5,6,7]
- 那么第二次讀取時就能夠讀到事務trx_id=3的更新內容,但讀不到事務trx_id=6的更新內容,從而實現讀已提交。
-
- RR
- 第一次讀數據時才生成一個ReadView,后面一直用該ReadView
- 由于每次讀數據使用的都是同一個ReadView,所以每次可見的版本是相同的,也就是可重復讀。
- RC