最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

一文精通MVCC機(jī)制

2023-02-18 00:19 作者:maotb  | 我要投稿

MVCC(Multi-Version Concurrency Control)多版本并發(fā)控制機(jī)制

使用串行化隔離級(jí)別時(shí),mysql會(huì)將所有的操作加鎖互斥,來(lái)保證并發(fā)安全。這種方式必然降低并發(fā)性能。mysql在讀已提交可重復(fù)讀隔離級(jí)別下,對(duì)一行數(shù)據(jù)的讀和寫兩個(gè)操作默認(rèn)是不會(huì)通過(guò)加鎖互斥來(lái)保證隔離性,避免了頻繁加鎖互斥。那么具體是如何實(shí)現(xiàn)的呢?首先要了解兩個(gè)概念。

準(zhǔn)備

建表語(yǔ)句

CREATE TABLE `product` ( ?`id` int NOT NULL AUTO_INCREMENT, ?`name` varchar(255) DEFAULT NULL, ?`price` int DEFAULT NULL, ?PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8;

undo日志版本鏈

  1. 我們向product表插入一條數(shù)據(jù)

INSERT INTO mysql_demo.product (id, name, price) VALUES (1, 'apple', 10);

此時(shí)mysql會(huì)同時(shí)向undo日志里寫入一條記錄。 trx_id為插入操作的事務(wù)id。這里隨便寫了一個(gè)80,意思一下。 roll_pointer后面再說(shuō)。

  1. 這時(shí)候又來(lái)了一個(gè)事務(wù),對(duì)數(shù)據(jù)進(jìn)行了修改。比如事務(wù)id 300,修改price為20。此時(shí)mysql同樣會(huì)在undo日志里寫入一條記錄。并且roll_pointer會(huì)指向前一條記錄

  1. 以此類推,后續(xù)又有新的事務(wù)來(lái)操作這條記錄,就會(huì)形成一條版本鏈,這條鏈就是undo日志版本鏈。

每條數(shù)據(jù)對(duì)應(yīng)著有一個(gè)undo日志版本鏈。

對(duì)于insert和update操作,mysql會(huì)向undo日志里添加一條記錄。select操作不會(huì)產(chǎn)生記錄。

對(duì)于刪除的情況可以認(rèn)為是update的特殊情況,會(huì)將版本鏈上最新的數(shù)據(jù)復(fù)制一份,然后將trx_id修改成刪除操作的trx_id,同時(shí)在該條記錄的頭信息(record header)里的(deleted_flag)標(biāo)記位寫上true,來(lái)表示當(dāng)前記錄已經(jīng)被刪除,在查詢時(shí)按照上面的規(guī)則查到對(duì)應(yīng)的記錄如果delete_flag標(biāo)記位為true,意味著記錄已被刪除,則不返回?cái)?shù)據(jù)。

在來(lái)看下什么是read view。

一致性試圖read view機(jī)制

read view的生成

  • 可重復(fù)讀隔離級(jí)別:事務(wù)開(kāi)啟后,首次執(zhí)行任何select時(shí)會(huì)生成當(dāng)前事務(wù)的read-view,在事務(wù)結(jié)束前不會(huì)變化。

  • 讀已提交隔離級(jí)別:事務(wù)開(kāi)啟后,每次執(zhí)行select時(shí)都會(huì)重新生成read-view。

read view的組成

這個(gè)視圖由執(zhí)行查詢時(shí)所有未提交事務(wù)id數(shù)組(數(shù)組里最小的id為min_id)已創(chuàng)建的最大事務(wù)id(max_id)組成。

我們來(lái)舉個(gè)例子。

  1. Transaction 80: 開(kāi)啟事務(wù),插入一條記錄。并且commit;

  2. Transaction 100:開(kāi)啟事務(wù),執(zhí)行update。生成事務(wù)id 100。這里需要注意begin和select不會(huì)生成事務(wù)id,所以加了一條無(wú)關(guān)的update,生成事務(wù)id。update內(nèi)容可以忽略。

begin/start transaction 命令并不是一個(gè)事務(wù)的起點(diǎn),在執(zhí)行到它們之后的第一個(gè)修改操作InnoDB表的語(yǔ)句,事務(wù)才真正啟動(dòng),才會(huì)向mysql申請(qǐng)事務(wù)id

mysql內(nèi)部是嚴(yán)格按照事務(wù)的啟動(dòng)順序來(lái)分配事務(wù)id的

  1. Transaction 200:同上

  2. Transaction 300:把價(jià)格修改成20了。并且commit了。

  3. select 1: select 不生成事務(wù)id。 事務(wù)開(kāi)啟后,首次執(zhí)行任何select時(shí)會(huì)生成當(dāng)前事務(wù)的read-view。

  4. Transaction 400:把價(jià)格修改成18了。

read view的組成 = 未提交事務(wù)id數(shù)組(數(shù)組里最小的id為min_id) + 已創(chuàng)建的最大事務(wù)id(max_id)組成

此時(shí)未提交事務(wù)id有100,200(80 已經(jīng)提交了)。最小的id為100。 已創(chuàng)建的最大事務(wù)id為300。(注意read view 是在第5步生成的,此時(shí)還沒(méi)有Transaction 400)

因此 read view為[100,200],300 ? min_id為100 ,max_id為300。 ?[100,200] 為視圖數(shù)組。

此時(shí)對(duì)應(yīng)的undo日志版本鏈如下

那么read view 的作用是什么呢?

read view的作用

根據(jù)上面的結(jié)果,我們可以將事務(wù)進(jìn)行分類。因?yàn)槭聞?wù)的id是有序遞增的。所以我們可以得出以下結(jié)論

  • 因?yàn)槲刺峤皇聞?wù)的最小id(min_id)為100,所以小于100的事務(wù)都是已提交的。( Transaction 80)

  • 因?yàn)橐褎?chuàng)建的最大事務(wù)id(max_id)為300,所以大于300的區(qū)域都是未開(kāi)啟事務(wù)。 (Transaction 400) ?未開(kāi)啟理解為在執(zhí)行select的時(shí)候沒(méi)有開(kāi)啟。

  • 介于min_id和max_id之間的事務(wù),包含了未提交和已提交的事務(wù)。 (Transaction 100,200,300)

那么mysql是如何通過(guò)read view和undo日志版本鏈實(shí)現(xiàn)并發(fā)事務(wù)之間的隔離的呢?那就需要看下版本鏈比對(duì)規(guī)則了。

版本鏈比對(duì)規(guī)則

事務(wù)里的每一條select都需要從對(duì)應(yīng)版本鏈里的最新數(shù)據(jù)開(kāi)始逐條跟read-view做比對(duì),按照比對(duì)規(guī)則得到最終的快照結(jié)果。下面我們來(lái)看下版本鏈比對(duì)規(guī)則。

  1. 如果 row 的 trx_id 落在綠色部分( trx_id

  2. 如果 row 的 trx_id 落在灰色部分( trx_id>max_id ),表示這個(gè)版本是由將來(lái)啟動(dòng)的事務(wù)生成的

    1. row 的 trx_id 就是當(dāng)前自己的事務(wù)是可見(jiàn)的;

    2. 否則不可見(jiàn);

  3. 如果 row 的 trx_id 落在黃色部分(min_id <=trx_id<= max_id),那就包括兩種情況

    1. 若 row 的 trx_id 在視圖數(shù)組中,表示這個(gè)版本是由還沒(méi)提交的事務(wù)生成的,

      1. 若 row 的 trx_id 就是當(dāng)前自己的事務(wù)是可見(jiàn)的

      2. 否則不可見(jiàn);

    2. 若 row 的 trx_id 不在視圖數(shù)組中,表示這個(gè)版本是已經(jīng)提交了的事務(wù)生成的,可見(jiàn)。

知道了版本鏈的比對(duì)規(guī)則,下面我們通過(guò)實(shí)例來(lái)看下,mysql的MVCC機(jī)制是如何工作的。

實(shí)戰(zhàn)演練

可重復(fù)讀Repeatable-Read(RR)

我們先以可重復(fù)讀Repeatable-Read(RR)為例

可重復(fù)讀隔離級(jí)別:事務(wù)開(kāi)啟后,首次執(zhí)行任何select時(shí)會(huì)生成當(dāng)前事務(wù)的read-view,在事務(wù)結(jié)束前不會(huì)變化。

案例一

我們先以上面的情況為例來(lái)進(jìn)行分析。此時(shí)的情況如下:

  • read view為 [100,200],300

  • undo日志版本鏈如下

  • 套用版本鏈比對(duì)規(guī)則

    1. 首先在版本鏈中找到最新數(shù)據(jù)。

    2. Transaction 300,trx_id = max_id。此時(shí)繼續(xù)比對(duì), trx_id 不在視圖數(shù)組中,可見(jiàn)。

    3. 返回Transaction 300記錄的數(shù)據(jù)信息。price = 20;

案例二

Transaction 400,在第10行執(zhí)行了一次update。

Transaction 100,在第11,12行執(zhí)行了兩次update。然后select 1 13行執(zhí)行了一次select。 我們來(lái)分析下這個(gè)select。

  • 因?yàn)镽R隔離級(jí)別首次執(zhí)行任何select時(shí)會(huì)生成當(dāng)前事務(wù)的read-view,在事務(wù)結(jié)束前不會(huì)變化。所以read view為 [100,200],300。沒(méi)有變化。

  • undo日志版本鏈如下

  • 套用版本鏈比對(duì)規(guī)則

    1. 首先在版本鏈中找到最新數(shù)據(jù)。

    2. Transaction 100,trx_id = min_id。繼續(xù)分析 trx_id 在視圖數(shù)組中,表示這個(gè)版本是由還沒(méi)提交的事務(wù)生成的,不可見(jiàn)。

    3. 第二行Transaction 100,分析同上

    4. 第三行Transaction 400, trx_id > max_id,不可見(jiàn)。

    5. Transaction 300,trx_id = max_id。此時(shí)繼續(xù)比對(duì),trx_id 不在視圖數(shù)組中,可見(jiàn)。

    6. 返回Transaction 300記錄的數(shù)據(jù)信息。price = 20;

案例三

繼續(xù)向下Transaction 100,在第15行commit。Transaction 200,在第15,16行執(zhí)行了兩次update。然后select1 17行執(zhí)行了一次select。 我們來(lái)分析下這個(gè)select。

  • 因?yàn)镽R隔離級(jí)別首次執(zhí)行任何select時(shí)會(huì)生成當(dāng)前事務(wù)的read-view,在事務(wù)結(jié)束前不會(huì)變化。所以read view為 [100,200],300。沒(méi)有變化。

  • undo日志版本鏈如下

  • 套用版本鏈比對(duì)規(guī)則

    1. 首先在版本鏈中找到最新數(shù)據(jù)。

    2. Transaction 200,min_id < trx_id < max_id。繼續(xù)分析 trx_id 在視圖數(shù)組中,表示這個(gè)版本是由還沒(méi)提交的事務(wù)生成的,不可見(jiàn)。

    3. 下一行Transaction 200,分析同上.

    4. Transaction 100,trx_id = min_id。繼續(xù)分析 trx_id 在視圖數(shù)組中,表示這個(gè)版本是由還沒(méi)提交的事務(wù)生成的,不可見(jiàn)。

    5. 下一行Transaction 100,分析同上。

    6. 下一行Transaction 400, trx_id > max_id,不可見(jiàn)。

    7. Transaction 300,trx_id = max_id。此時(shí)繼續(xù)比對(duì),trx_id 不在視圖數(shù)組中,可見(jiàn)。

    8. 返回Transaction 300記錄的數(shù)據(jù)信息。price = 20;

案例四

繼續(xù)select2 17行執(zhí)行了一次select。 我們來(lái)分析下這個(gè)select。

  • RR隔離級(jí)別首次執(zhí)行任何select時(shí)會(huì)生成當(dāng)前事務(wù)的read-view。read view為 [200,400],400。

  • undo日志版本鏈如下

  • 套用版本鏈比對(duì)規(guī)則

    1. 首先在版本鏈中找到最新數(shù)據(jù)。

    2. Transaction 200,trx_id = min_id。繼續(xù)分析 trx_id 在視圖數(shù)組中,表示這個(gè)版本是由還沒(méi)提交的事務(wù)生成的,不可見(jiàn)。

    3. 下一行Transaction 200,分析同上.

    4. Transaction 100,trx_id < min_id。表示這個(gè)版本是已提交的事務(wù)生成的,這個(gè)數(shù)據(jù)是可見(jiàn)的;

    5. 返回 price = 16。

案例五

我們?cè)賮?lái)看一下如果select1 如果有update操作(update操作會(huì)創(chuàng)建事務(wù)id,我們假設(shè)是 500)。Transaction 500 此時(shí)是如何讀取到更新后的數(shù)據(jù)的。

來(lái)分析下15行。

  • RR隔離級(jí)別首次執(zhí)行任何select時(shí)會(huì)生成當(dāng)前事務(wù)的read-view,在事務(wù)結(jié)束前不會(huì)變化。read view為 [100,200],300。

  • undo日志版本鏈如下

  • 套用版本鏈比對(duì)規(guī)則

    1. 首先在版本鏈中找到最新數(shù)據(jù)。

    2. Transaction 400,trx_id > max_id(read view是第一次select時(shí)生成的,此時(shí)max_id仍然是 300)。表示這個(gè)版本是由將來(lái)啟動(dòng)的事務(wù)生成的,是不可見(jiàn)的

    3. Transaction 500,trx_id > max_id。表示這個(gè)版本是由將來(lái)啟動(dòng)的事務(wù)生成的,但row 的 trx_id 就是當(dāng)前自己的事務(wù)是可見(jiàn)的;所以可見(jiàn)

    4. 返回 price = 8。

結(jié)論:通過(guò)以上案例,我們可以知道。 MVCC機(jī)制在RR中首次查詢時(shí)會(huì)固定read view。后續(xù)和其他事務(wù)隔離開(kāi)了,其他事務(wù)對(duì)數(shù)據(jù)的操作不會(huì)影響到當(dāng)前事務(wù)。

讀已提交Read-Committed(RC)

我們?cè)僖宰x已提交Read-Committed(RC)為例

讀已提交隔離級(jí)別:事務(wù)開(kāi)啟后,每次執(zhí)行select時(shí)都會(huì)重新生成read-view。

案例一

第9行沒(méi)有變化,我們來(lái)看第13行。

read view的組成 = 未提交事務(wù)id數(shù)組(數(shù)組里最小的id為min_id) + 已創(chuàng)建的最大事務(wù)id(max_id)組成

未提交事務(wù)id數(shù)組 100,200,400 ; ? ?min_id 100 ; max_id 400

  • read view為 [100,200,400],400。

  • undo日志版本鏈如下

  • 套用版本鏈比對(duì)規(guī)則

    1. 首先在版本鏈中找到最新數(shù)據(jù)。

    2. Transaction 100,trx_id = min_id。繼續(xù)分析 trx_id 在視圖數(shù)組中,表示這個(gè)版本是由還沒(méi)提交的事務(wù)生成的,不可見(jiàn)。

    3. 下一行Transaction 100,分析同上.

    4. Transaction 400, trx_id = max_id。繼續(xù)分析 trx_id 在視圖數(shù)組中,表示這個(gè)版本是由還沒(méi)提交的事務(wù)生成的,不可見(jiàn)。

    5. Transaction 300,min_id < trx_id< max_id。不在視圖數(shù)組中,表示這個(gè)版本是已經(jīng)提交了的事務(wù)生成的,可見(jiàn)。

    6. 返回 price = 20。

案例二

來(lái)看第17行。

read view的組成 = 未提交事務(wù)id數(shù)組(數(shù)組里最小的id為min_id) + 已創(chuàng)建的最大事務(wù)id(max_id)組成

未提交事務(wù)id數(shù)組 200,400 ; ? ?min_id 200 ; max_id 400

  • read view為 [200,400],400。

  • undo日志版本鏈如下

  • 套用版本鏈比對(duì)規(guī)則

    1. 首先在版本鏈中找到最新數(shù)據(jù)。

    2. Transaction 200, trx_id = min_id。繼續(xù)分析 trx_id 在視圖數(shù)組中,表示這個(gè)版本是由還沒(méi)提交的事務(wù)生成的,不可見(jiàn)。

    3. 同上

    4. Transaction 100, trx_id

    5. 返回 price = 16。

OK,就分析到這里吧。希望對(duì)你有所幫助!



一文精通MVCC機(jī)制的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
澳门| 阳西县| 宁夏| 黔江区| 呼图壁县| 元朗区| 德州市| 南部县| 禹城市| 衡水市| 启东市| 当阳市| 河东区| 大同市| 土默特右旗| 德州市| 临沭县| 山阳县| 定边县| 广河县| 布尔津县| 德兴市| 昌平区| 平果县| 景谷| 威海市| 池州市| 崇文区| 深州市| 正阳县| 大竹县| 五河县| 武冈市| 云浮市| 淄博市| 休宁县| 临泽县| 黔南| 霍城县| 民勤县| 历史|