MySql 丁奇 學習筆記 MVCC

2021-10-10 23:36:47 字數 4128 閱讀 6619

問題0

有如下表結構,分析下面三個事務讀到的資料是甚麼?

create table `t`

engine=innodb;

insert into t values(1,1)

;

事務a

事務b事務c

start transaction with consistent snapshot;

start transaction with consistent snapshot;

update t set k=k+1 where id=1;

update t set k=k+1 where id=1;

select k from t where id=1;

select k from t where id=1;

commit;

commit;

注意

begin/start transaction 命令並不是乙個事務的起點,在執行到它們後的第乙個操作innodb表的語句,事務才真正啟動.

如果想要馬上啟動乙個事務,可以使用start transaction with consistent snapshot;

事務c使用自動提交autocommit=1

三個事務結束後最終能讀到的資料如下

事務a事務b

事務ck=1

k=3k=2

首先mysql中有兩個檢視的概念

在rr隔離級別下,事務開始時會對整庫拍乙個"快照",之後的操作都是基於這個快照進行的,所以在rr隔離級別下看不到後來事務提交的資料.

在rc隔離級別下,每條語句執行前都會拍乙個"快照",所以能看到後來事務提交的資料.

innodb裡面每乙個事務都有乙個唯一的事務id,即transaction id.它是在事務開始時向innodb事務系統申請的,而且是按照申請順序嚴格遞增的.

每行資料也有多個版本, 每次事務更新資料時都會生成乙個新的版本, 並將版本號記錄在乙個隱藏的資料列db_trx_id中. 同時舊的資料要保留, 並且在新的資料版本中可以直接拿到它(通過另乙個隱藏列db_roll_pt).

所以一行資料被多個事務連續更新的狀態如下圖:

上圖為乙個行資料的四個版本, 當前版本是v4, k=12, 由57號事務更新的, db_trx_id=57.

db_roll_pt相當於乙個指標, 指向的就是undo log中的記錄, 而不同版本的資料並不是物理上真實存在的, 而是每次需要的時候根據當前版本和undo log計算出來的.

由rr的定義, 乙個事務啟動的時候, 能夠看到所有已經提交的事務結構. 但是之後, 在這個事務執行期間, 其他事務提交的更新是不可見的.

首先事務開始時innodb為事務構建了乙個陣列, 用來儲存這個事務啟動瞬間,當前所有』活躍』的事務id,'活躍』是指事務開始了但是還沒有提交.

陣列裡事務id的最小值記位低水位

當前系統裡面已經建立過的事務id的最大值加一記位高水位

由這個陣列和高低水位,就組成了當前事務的一致性檢視.

這個檢視把所有的db_trx_id分為了如下幾種情況:

這樣一來,對於當前事務啟動瞬間,乙個資料版本的db_trx_id由如下幾種可能:

在綠色部分, 表示這個版本時已經提交的事務或者是當前事務自己生成的, 這個資料是可見的

在紅色部分, 表示這個版本是由將來啟動的事務生成的(注意高水位的定義)., 是不可見的

在黃色部分,有兩種情況:

a. 若db_trx_id在陣列中, 表示這個版本是未提交事務生成的, 不可見

b. 若db_trx_id不在陣列中, 表示這個版本是已提交事務生成的, 可見

舉個栗子, 在下面這張圖中, 如果有乙個事務, 其低水位是33, 那麼它訪問這行資料時, 就會通過db_trx_id 通過 undo log計算並找出它的前乙個版本v3的資料. 在這個事務中, 看到的k值為11.

回到最開始的栗子,如下圖

事務a,事務b,事務c依次開啟並操作同一行資料,資料庫中該行的當前版本為90, (id, k) = (1, 1).

事務a: transaction_id=99, read view 陣列: [90, 99];

事務b: transaction_id=105, read view 陣列: [90, 99, 105];

事務c: transaction_id=180, read view 陣列: [90, 99, 105, 180];

事務c先更新資料(id, k)為(1, 2)

事務b更新資料(id, k),由於事務c在事務b之前已經將資料更新為(1, 2),所以事務b更新資料時需要遵循當前寫機制(一會再說當前寫),所以再事務c的基礎上更新資料為(1, 3)

事務a查詢資料,發現當前(最新)資料的trx_id=105,高於事務a陣列的高水位,不可見,於是向上查詢,發現前乙個版本資料的trx_id=180,高於事務a陣列的高水位,不可見,於是再向上查詢,找到trx_id=90版本的資料,可見,於是事務a查到的資料為(id, k) = (1, 1)

在事務b的更新操作前查詢資料,得到的也是(1, 1),但是在更新操作後再查詢資料,得到的就是(1, 3)了

這裡就需要介紹當前寫機制,innodb事務更新資料時需要獲取行鎖,再更新資料操作前需要拿到資料庫中最新版本的資料,再進行更新,也就是當前寫。同樣的,如果一條查詢語句也加了行鎖,比如:select k from t where id=1 for update;或者select k from t where id=1 lock in share mode;,也會觸發當前寫機制。

所以事務b在更新資料時,拿到的最新資料是事務c已經提交的資料,並在此基礎上更新k值。

所以上述過程中的第5步的結果是為甚麼就清楚了吧:更新操作前讀到的是trx_id=90的資料,更新操作後讀到的是自己更新的資料,trx_id=180。

在事務b的更新操作前查詢資料,得到的也是(1, 1),但是在更新操作後再查詢資料,得到的就是(1, 3)了

事務a

事務b事務c

start transaction with consistent snapshot;

start transaction with consistent snapshot;

start transaction with consistent snapshot;

update t set k=k+1 where id=1;

update t set k=k+1 where id=1;

select k from t where id=1;

commit;

select k from t where id=1;

commit;

commit;

事務c沒有開啟自動提交,而是在事務b更新操作後提交的資料,這種情況下由於事務c先獲得了資料行的寫鎖,所以事務b必須等待事務c提交並釋放了寫鎖後,獲取了行鎖才能進行更新操作。

所以得到的結果和之前的栗子是一樣的,只不過事務b的更新操作會因為得不到行鎖而阻塞。

丁奇 mysql 丁奇 MySQL實戰讀書筆記13

1 為啥刪除了表的一半數8據,表文檔案大小沒變化?因為delete 命令其實只是把記錄的位置,或者資料頁標記為了 可復用 但磁碟檔案的大小是不會變的。也可以認為是一種邏輯刪除,所以物理空間沒有實際釋放,只是標記為可復用,表檔案的大小當然是不變的啦!2 表的資料資訊存在 表資料資訊可能較小也可能巨大無...

丁奇mysql實戰 丁奇 MySQL實戰讀書筆記5

在下面這個表 t 中,如果我執行 select from t where k between 3 and 5,需要執行幾次樹的搜尋操作,會掃瞄多少行?mysql create table t id int primary key,k int not null default 0,s varchar ...

丁奇mysql實戰 丁奇 MySQL實戰讀書筆記1

這個專欄的內容是我工作以來看過講mysql最深入而且最細緻的,作者丁奇是前阿里資深技術專家,和褚霸 霸爺 一起研究資料庫7年之久,我覺得很有必要把這個專欄重新讀一下並做好筆記,以求共勉。特別注意由於丁奇在資料庫領域比較資深,所以很多知識他可能認為你至少應該是了解的,如果完全不了解某個知識點,建議先去...