mysql隔離級別實現(xiàn)原理探究
關于這個話題,在網(wǎng)上看到了多種說法,總是擼不通思路,于是決定自己探究,先把結(jié)論貼出來
未提交讀寫時加排他鎖,寫完釋放;(讀時不加鎖;)
提交讀寫時加排他鎖,事務結(jié)束后釋放
讀時通過mvcc,訪問的是創(chuàng)建版本最大&&刪除版本為空的記錄
重復讀寫時加排他鎖,事務結(jié)束后釋放
讀時通過mvcc,訪問的是創(chuàng)建版本小于等于當前版本&&(刪除版本大于當前版本 ||?刪除版本為空)的記錄
結(jié)論純粹是個人推測出來的,憑個人的實驗驗證和進一步的猜想得來,但不一定就是正確的!!!
簡單驗證一下提交讀情況下,寫時加排他鎖,事務結(jié)束后釋放的情況:
事務1和事務2是基于相同的背景的,關閉了自動提交,設置隔離級別為提交讀,然后開始了事務。
接下來我們可以看到,事務2對id為1的記錄做了修改,這個時候并沒有提交,此時事務1也嘗試對該記錄做修改,但卻失敗了,報錯說鎖等待超時。
之后我將事務2提交了,事務1再次執(zhí)行修改語句,成功了。
從上面的實驗可以得出一個結(jié)論,隔離級別為提交讀的情況下,對記錄的修改會加上排他鎖,且直到事務結(jié)束時才釋放。
其他隔離級別下的寫時加鎖情況可以依據(jù)上面的實驗去推出結(jié)論,請有興趣的讀者自行驗證。
至于讀時加鎖或者借助mvcc機制的情況,等我進一步學習mvcc后再來補充。
https://blog.csdn.net/aigoogle/article/details/42558075谷歌搜到這篇博文與本文的思想是一樣的。
繼續(xù)探究了mysql的mvcc實現(xiàn)后,有了一些新的理解:
在mysql內(nèi)部維護著很多的日志表,其中有一個叫做undo日志表(記錄原始數(shù)據(jù),主要用于事務回滾操作,也可以用于mvcc機制),同時還使用了一個叫做read view的表來做可見性判斷。
在mysql中,mvcc是通過在表中插入三個隱藏字段實現(xiàn)的,這三個隱藏字段如下:
6字節(jié)的事務ID? DB_TRX_ID)?
7字節(jié)的回滾指針(DB_ROLL_PTR)?
隱藏的ID
假設我們有記錄如下
現(xiàn)在我們開啟一個事務1,并對該記錄做修改
這個時候如果有另外一個事務2,并且這個事務2的版本小于事務1,現(xiàn)在,事務1要查詢這條記錄。
在真實表中,數(shù)據(jù)已被修改,但因為事務2的版本小于事務1的版本,這條修改對事務2來說是不可見的,于是就根據(jù)這條記錄的回滾id找到了undo log中原來的數(shù)據(jù)。
在真實情況中,并發(fā)的事務數(shù)量大,對數(shù)據(jù)的可見性需要統(tǒng)一管理,于是就有了read view。
在可重復讀級別下,開啟一個事務,這個時候mysql就要將此刻所有活躍的事務拷貝到一個新的read view表中, 假定其中最大最小的事務版本號分別為max_id,min_id,現(xiàn)要查詢的記錄版本號為trx_id。有以下幾種情況:
1、trx_id<min_id,說明在開啟事務前,記錄已存在,可見。
2、trx_id>max_id,說明在開啟事務時,記錄還不存在,不可見。
3、min_id<trx_id<max_id,這時候我們需要遍歷read view表,看看是否有對應的事務版本號。如果有,說明在開啟事務時,記錄處于活躍狀態(tài),此時需要對照回滾id,找到undo表中相應的記錄。如果沒有,說明開啟事務時,修改了該條記錄的另一個事務已經(jīng)結(jié)束,記錄可見。
在提交讀的級別下,不同的地方在于每次執(zhí)行語句時,都會重新計算一個新的read view表,這樣就可以達到讀取已提交記錄的目的。
參考文章:http://www.imooc.com/article/17290
https://liuzhengyang.github.io/2017/04/18/innodb-mvcc/