你是否對 MySQL 數(shù)據(jù)庫中的事務(wù)已經(jīng)有所了解?看下面這張圖,按照 1~6 的順序依次執(zhí)行,在RR隔離級別下,事務(wù) A 和事務(wù) B 各自輸出的 num 值是多少嗎?
我們預(yù)先創(chuàng)建好這樣一張表并初始化一條數(shù)據(jù):
CREATETABLE`test1`( `id`int(11)NOTNULLAUTO_INCREMENTCOMMENT'主鍵Id', `num`int(11)NULLCOMMENT'數(shù)量', PRIMARYKEY(`id`) )ENGINE=InnoDB; insertintotest1(id,num)values(1,1);
然后開始按上圖的順序執(zhí)行各個事務(wù),這需要我們打開3個操作窗口來分別執(zhí)行 A、B、C 三個事務(wù):
事務(wù) A:
事務(wù) B:
事務(wù) C:
按照上圖的執(zhí)行順序執(zhí)行 commit,其中事務(wù) C 是自動提交事務(wù)的,不需要我們顯示的 commit,事務(wù) A、B 的輸出結(jié)果如下:
事務(wù)A:num=1 事務(wù)B:num=3
為什么是這樣輸出?
它的背后其實是:MVCC(多版本并發(fā)控制)、consistent read(一致性讀)、locking reads(鎖定讀)等 MySQL 數(shù)據(jù)庫底層知識。
1、MVCC
MySQL 數(shù)據(jù)庫官網(wǎng)文檔是這樣來描述 MVCC 的:
多版本控制: 指的是一種提高并發(fā)的技術(shù)。最早的數(shù)據(jù)庫系統(tǒng),只有讀讀之間可以并發(fā),讀寫,寫讀,寫寫都要阻塞。引入多版本之后,只有寫寫之間相互阻塞,其他三種操作都可以并行,這樣大幅度提高了 InnoDB 的并發(fā)度。在內(nèi)部實現(xiàn)中,與 Postgres 在數(shù)據(jù)行上實現(xiàn)多版本不同,InnoDB 是在 undolog 中實現(xiàn)的,通過 undolog 可以找回數(shù)據(jù)的歷史版本。
找回的數(shù)據(jù)歷史版本可以提供給用戶讀(按照隔離級別的定義,有些讀請求只能看到比較老的數(shù)據(jù)版本),也可以在回滾的時候覆蓋數(shù)據(jù)頁上的數(shù)據(jù)。在 InnoDB 內(nèi)部中,會記錄一個全局的活躍讀寫事務(wù)數(shù)組,其主要用來判斷事務(wù)的可見性。
目前來看 MVCC 的實現(xiàn)依賴于:
隱藏字段(DB_TRX_ID、DB_ROLL_PTR)
回滾日志(undo log)
一致性讀(consistent read)
你也可以這樣去理解 MVCC:一個事務(wù)對數(shù)據(jù)進(jìn)行更新操作時候,先把舊的數(shù)據(jù)放到一個單獨的地方(回滾段),其他事務(wù)讀取數(shù)據(jù)時候,根據(jù) DB_TRX_ID、DB_ROLL_PTR 計算出 undo log 鏈中當(dāng)前版本的數(shù)據(jù)。
2、一致性讀(consistent read)
繼續(xù)看官方文檔對 consistent read 的描述:
直譯:
一個讀操作使用基于某個時刻的快照信息來顯示查詢結(jié)果,而不考慮同時運行的其他事務(wù)所執(zhí)行的更改。如果查詢到的數(shù)據(jù)被其他事務(wù)所更改,則根據(jù) undo log 中的內(nèi)容來重建原始數(shù)據(jù)。這種技術(shù)避免了一些通過強(qiáng)制事務(wù)等待其他事務(wù)完成而降低并發(fā)性的鎖定問題。
在 RR 級別下,首次讀操作被執(zhí)行時候創(chuàng)建一致性讀視圖 ReadView,事務(wù)的后續(xù)讀都基于該視圖的數(shù)據(jù);
在 RC 級別下,每一次讀操作都會創(chuàng)建一個最新的 ReadView,因此每次 select 讀都可以獲取到當(dāng)前已提交事務(wù)的最新數(shù)據(jù)。
“一致性讀”是 InnoDB 引擎在 RC 和 RR 隔離級別下處理 select 語句的默認(rèn)模式。因為一個“一致性讀”是不需要對它訪問的表設(shè)置任何的鎖,當(dāng)對表執(zhí)行“一致性讀”時候,其他會話可以自由的修改這些表。
另外:
讀未提交(read uncommitted)、串行化(serializable)是不需要依賴 MVCC 的,讀未提交直接每次都讀取當(dāng)前數(shù)據(jù)的最新值即可。而 serializable 是直接采用加鎖的操作讓所有的事務(wù)都串行化執(zhí)行,犧牲了并發(fā)能力。
一致性讀的實現(xiàn)方式:
每個事務(wù)啟動的瞬間,都會構(gòu)建一個數(shù)組(m_ids),用來記錄目前所有“活躍事務(wù)”(事務(wù)啟動了,但是還沒提交)的 ID;
數(shù)組中的最小事務(wù) ID 為低水位;
數(shù)組中的最大事務(wù) ID + 1 為高水位;
數(shù)據(jù)版本可見性規(guī)則:當(dāng)前數(shù)據(jù)某個版本是否可見,取決于當(dāng)前數(shù)據(jù)的 DB_TRX_ID 以及這個一致性視圖數(shù)組中記錄的事務(wù) ID 做對比來判斷:低水位以前的數(shù)據(jù)版本可見,高水位以后的數(shù)據(jù)版本不可見,低水位和高水位之間得查看當(dāng)前數(shù)據(jù)版本的 DB_TRX_ID 是否存在數(shù)組中,若存在意味著事務(wù)未提交,不可見,若不存在意味著事務(wù)已提交,可見。
那按照一致性讀的理解,事務(wù)B已經(jīng)創(chuàng)建了自己的快照數(shù)據(jù)了,它的輸出應(yīng)該是 num = 2 呀,為什么會是 num=3?
可是如果不是 num=3,那么已經(jīng)提交的事務(wù) C 的操作不就丟失了嗎?(產(chǎn)生丟失更新問題)
這里又涉及到一個知識點:
更新數(shù)據(jù)都是先讀后寫的,而這個讀,只能讀當(dāng)前的值,稱為“當(dāng)前讀”(current read)。
3、當(dāng)前讀(current reads)
也叫做鎖定讀(locking reads)
InnoDB 引擎支持兩種方式的鎖定讀以提供額外的安全性(MySQL 5.7 版本):
#讀鎖(S鎖,共享鎖) SELECT...LOCKINSHAREMODE; #寫鎖(X鎖,排他鎖) SELECT...FORUPDATE;
鎖定讀會在被讀取的數(shù)據(jù)上加一把共享鎖,其他事務(wù)可以讀取記錄,但是不可以修改記錄,直到當(dāng)前事務(wù)提交。
鎖定讀驗證:
為什么要有鎖定讀?
如果你在一個事務(wù)中先查詢了一個數(shù)據(jù),然后插入或者更新相關(guān)的數(shù)據(jù),這個時候來了一個事務(wù)B同時更新或者刪除你要查詢的記錄,就會出現(xiàn)幻讀問題了。
這也是為什么 MVCC 不能完全解決幻讀的問題,而是需要 MVCC + 行鎖 + 間隙鎖(next-key lock)的方式。
4、事務(wù) A、B、C 的執(zhí)行流程
繼續(xù)看開頭的第一張圖:
starttransactionwithconsistentsnapshot;
這條 SQL 語句可以立即啟動事務(wù),創(chuàng)建當(dāng)前事務(wù)的一致性讀快照。效果等同于 start transaction 然后馬上執(zhí)行 select 語句。
我們接下來看看文章開頭的三個事務(wù)對數(shù)據(jù)行的修改流程,按照步驟 1~6 的操作如下:
如果大家細(xì)致的查看上圖的三個事務(wù)的穿插執(zhí)行流程,可以發(fā)現(xiàn),A、B、C 三個事務(wù)無論是 commit 還是 rollback,都是可以最終得到正確的數(shù)據(jù)。
這就是 InnoDB 引擎下的多版本并發(fā)控制(MVCC)的實現(xiàn)原理。
總結(jié)以下幾個關(guān)鍵點:
每一個事務(wù)都會創(chuàng)建一個數(shù)據(jù)快照,快照創(chuàng)建的時機(jī)根據(jù)隔離級別的不同有所區(qū)別;
每一個事務(wù)都會生成一個全局唯一的 DB_TRX_ID,用于標(biāo)記當(dāng)前版本;
DB_ROLL_PTR 是回滾指針的意思,結(jié)合 DB_TRX_ID 來最終確定我要拿到的數(shù)據(jù);
DB_TRX_ID、DB_ROLL_PTR、undo log 這三個值來控制數(shù)據(jù)的版本;
update、delete 操作都是先讀后寫,這個讀屬于鎖定讀(當(dāng)前讀)。
審核編輯:劉清
-
TRX
+關(guān)注
關(guān)注
0文章
3瀏覽量
9224 -
MySQL
+關(guān)注
關(guān)注
1文章
817瀏覽量
26628 -
MYSQL數(shù)據(jù)庫
+關(guān)注
關(guān)注
0文章
96瀏覽量
9412 -
MVCC
+關(guān)注
關(guān)注
0文章
13瀏覽量
1481
原文標(biāo)題:MySQL 底層之 MVCC、回滾段、一致性讀、鎖定讀
文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論