之前寫了一篇文章以為對MVCC的大致原理有些了解了。今天看了《高性能MySQL》的時候,深究了一下read view的問題,發現還是蠻有意思的。 特別畫了一張圖來確認一下。 本文是上一篇MySQL事務和MVCC簡介的后續,建議先了解上一篇文章以后再閱讀本文。 上一篇文
之前寫了一篇文章以為對MVCC的大致原理有些了解了。今天看了《高性能MySQL》的時候,深究了一下read view的問題,發現還是蠻有意思的。
特別畫了一張圖來確認一下。
本文是上一篇MySQL事務和MVCC簡介的后續,建議先了解上一篇文章以后再閱讀本文。
上一篇文章簡單描述了MVCC的相關情況,但是沒有詳細說,read view是什么結構,并且它到底是怎么工作的。
比如,我們在show engine innodb status可以看到如下內容:
要理解這個,我們首先要知道:
read view其實就是一個保存事務ID的list列表。記錄的是本事務執行時,MySQL還有哪些事務在執行。
Read Repeatable(下文和圖中用RR表示)對應的是在每個事務啟動的時候創建 一個Read View。
Read Commit(下文和圖中用RC表示)對應的是每次執行SQL statement時候創建?一個Read View。
根據show engine innodb status的輸出是說看到這個事務的id是600。
對這個事務來說,trx id為596以下的所有事務修改的行數據,這個事務都可以看到,
trx id在601以上的事務修改的數據,這個事務都不應該讀取到。
596到601號事務,一共5個事務修改的數據無法確定是否能夠讀取。read view應該為這5個事務id集合的子集。
如果線程的隔離級別是RR:
按照show engine innodb status的輸出,600號事務在事務啟動的時候,MySQL告訴它:
596之前的所有事務都已經提交了(Trx read view will not see trx with id>= 0 601, sees <0 596),
由于事務本身是600號,那么對應的601號事務因為是在它后面啟動的,600號事務肯定無法提供讀取到數據(Trx read view will not see trx with id>= 0 601,?sees <0 596)。
read view表示的是事務開始時MySQL還有哪些事務在執行,就應該為{596,597,598,599}集合的子集,假設為{596,598},
根據read view,Innodb在讀取數據的時候需要判斷該行數據的修改事務號,判斷的方法為:
a)?如果行數據的修改事務號小于596,由于在事務啟動的時候596之前的所有線程都已經提交了,那么該行數據可讀。
b) 如果行數據的修改事務號大于601,那么該行數據肯定不可讀。如果事務號為600(即自己),本事務未提交,當然也是不可讀的。
為了保證在事務內任何時間讀取的數據都是一致的,需要根據行數據的undo信息回溯,每次回溯都需要進行a),b),c),d)的判斷,直到找到一個可讀的數據。
c) 如果行數據的修改事務號在read view里面{596,599},說明是該事務(600號)開始時沒有提交的數據修改,
為了保證在事務內任何時間讀取的數據都是一致的,需要根據行數據的undo信息回溯,每次回溯都需要進行a),b),c),d)的判斷,直到找到一個可讀的數據。
d)如果不在read view里面,即事務id號在{597,598}中,說明修改行數據是該事務(600號)開始時已經提交的數據修改,那么該行數據可讀。
圖1 MySQL read view 示意圖
如圖1。這個事務的行修改數據在{[0~595],597,598}是可讀區間,{596,599,600,[601~ +infinity]}是不可讀區間。
如果線程的隔離級別是RC,線程開始的時候,RC事務并不會做read view,此時開始的SQL跟上面RR的情況可能是一樣的。
但是過了一段時間如果601事務提交了,同樣的查詢,在RC下面提交,對應的show engine innodb status的信息可能稍微有點不同:
按照輸出,600號事務在語句“select sql_calc_found_rows * from b limit 5”發起的時候,MySQL告訴它:
596之前的所有事務都已經提交了(Trx read view will not see trx with id>= 0 601,?sees <0 596),
對應的,602號線程以及它之后的所有線程都還未提交(Trx read view?will not see trx with id>= 0 602,?sees <0 596)。
read view表示的是語句開始時MySQL還有哪些事務在執行(注意,這里跟RR為事務開始的時候的read view不同了),
在一個事務里面,每個SQL執行的時候,它的read view都可能是不同的。有可能事務啟動的時候的sql的read view為{596,598},
這個語句執行的時候,601事務提交了,read view為{596,598}。
注意,601號事務雖然在600事務后啟動,此時已經提交了行數據修改,它修改的數據,600號線程也可以讀到。
根據read view,InnoDB在讀取數據的時候需要判斷該行數據的修改事務號,判斷的方法為:
a)?如果行數據的修改事務號小于596,由于在語句啟動的時候596之前的所有線程都已經提交了,那么該行數據可讀。
b) 如果行數據的修改事務號大于等于602,那么該行數據肯定不可讀。如果事務號為600(即自己),本事務未提交,當然也是不可讀的。
為了保證讀到的是Commited的數據,需要根據行數據的undo信息回溯,每次回溯都需要進行a),b),c),d)的判斷,直到找到一個可讀的數據。
c) 如果行數據的修改事務號在read view里面{596,599},說明是該語句開始時沒有提交的數據修改,
為了保證讀到的是Commited的數據,需要根據行數據的undo信息回溯,每次回溯都需要進行a),b),c),d)的判斷,直到找到一個可讀的數據。
d)如果不在read view里面,即事務id號在{597,598}中,說明修改行數據是該語句開始時已經提交的數據修改,那么該行數據可讀。
如圖1。這個語句的修改行數據的事務id在{[0~595],597,598,601}是可讀區間,{596,599,600,[602~ +infinity]}是不可讀區間。
整體來說,這篇文章描述了在Read Readrepeatable和Read Commit環境下,MySQL根據Read View讀取數據的方法,來保證可重復讀和只讀到已經提交的數據。
原文地址:MVCC read view的問題, 感謝原作者分享。
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com