事務并發(fā)執(zhí)行遇到的問題
- 臟讀(未提交讀)
- 不可重復讀(已提交讀)
- 幻讀(讀出新紀錄)
事務隔離級別
隔離級別 | 臟讀 | 不可重復讀 | 幻讀 |
---|---|---|---|
未提交讀 | 可能 | 可能 | 可能 |
已提交讀 | —— | 可能 | 可能 |
可重復讀 | —— | —— | —— |
可串行化 | —— | —— | —— |
mysql在可重復讀的級別下,極大的程度上避免了幻讀厉萝。
版本鏈
對于InnoDB引擎來說柑营,每個主鍵索引記錄中包含了兩個隱藏列
隱藏列 | |
---|---|
trx_id | 記錄當前正在操作此條記錄的事務id |
roll_pointer | 修改時囚痴,記錄舊的版本到undo日志中, 當前列就是指向舊版本的指針,以便找到修改前的信息 |
示例
- 創(chuàng)建一張表
CREATE TABLE teacher ( number INT,
name VARCHAR(100), domain varchar(100), PRIMARY KEY (number)
) Engine=InnoDB CHARSET=utf8;
- 新增一條記錄辽俗,并且當前插入的事物id為60
INSERT INTO teacher VALUES(1, 'Jack', '源碼系列');
此刻生成一條記錄深啤,如下圖所示
- 之后有兩個80、120事務更新
trx_id(80) | trx_id(120) |
---|---|
BEGIN | |
BEGIN | |
UPDATE teacher SET name = 'Mark' WHERE number = 1; | |
UPDATE teacher SET name = 'James' WHERE number = 1; | |
COMMIT | |
UPDATE teacher SET name = 'King' WHERE number = 1; | |
UPDATE teacher SET name = '大飛' WHERE number = 1; | |
COMMIT |
每次記錄修改都有一條undo日志汛骂,每條undo日志都會一個roll_pointer屬性罕模。可以將這些undo日志串成鏈表帘瞭,我們把這種鏈表稱為版本鏈淑掌。
對該記錄每次更新后,都會將舊值放到一條 undo 日志中蝶念,就算是該記錄的 一個舊版本抛腕,隨著更新次數的增多,所有的版本都會被 roll_pointer 屬性連接成 一個鏈表媒殉,我們把這個鏈表稱之為版本鏈
ReadView
InnoDB 提出了一個 ReadView 的概念兽埃, 包含 4 個比較重要的內容:
id | 說明 |
---|---|
m_ids | 生成 ReadView 時當前系統(tǒng)中活躍的讀寫事務的事務 id 列表 |
min_trx_id | 生成 ReadView 時當前系統(tǒng)中活躍的讀寫事務中最小的事務 id,也就是 m_ids 中的最小值适袜。 |
max_trx_id | 生成 ReadView 時系統(tǒng)中應該分配給下一個事務的 id 值 |
creator_trx_id | 生成該 ReadView 的事務的事務 id |
訪問某條記錄時柄错,使用ReadView,遍歷版本鏈苦酱,通過下面規(guī)則判斷某個節(jié)點數據是否可以訪問售貌。
- 如果被訪問的 trx_id 屬性值與 ReadView 中的 creator_trx_id 值相同, 意味著當前事務在訪問它自己修改過的記錄疫萤,所以該版本可以被當前事務訪問颂跨。
- 如果被訪問版本的 trx_id 屬性值小于 ReadView 中的 min_trx_id 值,表明 生成該版本的事務在當前事務生成 ReadView 前已經提交扯饶,所以該版本可以被當 前事務訪問恒削。
- 如果被訪問版本的 trx_id 屬性值大于或等于 ReadView 中的 max_trx_id 值,表明生成該版本的事務在當前事務生成 ReadView 后才開啟尾序,所以該版本不 可以被當前事務訪問钓丰。
- 如果被訪問版本的 trx_id 屬性值在 ReadView 的 min_trx_id 和 max_trx_id 之間(min_trx_id <= trx_id < max_trx_id),那就需要判斷一下 trx_id 屬性值是不是在 m_ids 列表中每币,如果在携丁,說明創(chuàng)建 ReadView 時生成該版本的事務還是活躍的, 該版本不可以被訪問;如果不在兰怠,說明創(chuàng)建 ReadView 時生成該版本的事務已經 被提交梦鉴,該版本可以被訪問李茫。
- 如果某個版本的數據對當前事務不可見的話,那就順著版本鏈找到下一 個版本的數據肥橙,繼續(xù)按照上邊的步驟判斷可見性魄宏,依此類推,直到版本鏈中的最 后一個版本存筏。如果最后一個版本也不可見的話娜庇,那么就意味著該條記錄對該事務 完全不可見,查詢結果就不包含該記錄方篮。
在 MySQL 中名秀,已提交讀(RC)和可重復讀(RR)隔離級別的的一個非 常大的區(qū)別就是它們生成 ReadView 的時機不同。
- 已提交讀(RC)在每次讀取數據前都生成一個 ReadView藕溅。
- 可重復讀(RR)在第一次讀取數據時生成一個 ReadView匕得。
mysql默認的事務隔離級別是可重復讀(RC)
mysql可重復讀(RR)基本解決了幻讀問題,那什么情況下會出現幻讀問題呢巾表。
- 假設有T1汁掠,T2,兩個事務集币,T1先開啟事務考阱。
- T2插入一條記錄,并提交事務
- T1查詢T2插入的記錄鞠苟,很明顯查詢不到乞榨。
- 如果此刻T1更新了T2插入的那條記錄
- 此刻T1再去查詢那條記錄,這時是可以查到的当娱,這就是幻讀
這種情況在實際生產并不多吃既。